diff options
Diffstat (limited to 'src')
152 files changed, 27595 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..e82522d --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,68 @@ +# set the include path for X, qt and KDE +INCLUDES = $(all_includes) + +# these are the headers for your project +noinst_HEADERS = bookmarksdlg.h calltreedlg.h calltreemanager.h \ + configfrontend.h cscopefrontend.h cscopemsgdlg.h ctagsfrontend.h ctagslist.h \ + dirscanner.h dotfrontend.h editormanager.h editorpage.h editortabs.h encoder.h \ + filelist.h fileview.h frontend.h graphedge.h graphnode.h graphprefdlg.h \ + graphwidget.h historypage.h historyview.h kscope.h kscopeactions.h kscopeconfig.h \ + kscopepixmaps.h makedlg.h makefrontend.h newprojectdlg.h openprojectdlg.h prefcolor.h \ + preferencesdlg.h preffont.h preffrontend.h prefopt.h progressdlg.h project.h \ + projectbase.h projectfilesdlg.h projectmanager.h querypage.h querypagebase.h \ + queryresultsmenu.h queryview.h queryviewdlg.h queryviewdriver.h querywidget.h \ + scanprogressdlg.h searchlist.h searchresultsdlg.h symbolcompletion.h symboldlg.h \ + tabwidget.h treewidget.h + +# let automoc handle all of the meta source files (moc) +METASOURCES = AUTO + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kscope.pot + +KDE_ICON = kscope + +######################################################################### +# APPLICATION SECTION +######################################################################### +# this is the program that gets installed. it's name is used for all +# of the other Makefile.am variables +bin_PROGRAMS = kscope + +# the application source, library search path, and link libraries +kscope_SOURCES = autocompletionlayout.ui bookmarksdlg.cpp bookmarkslayout.ui \ + calltreedlg.cpp calltreelayout.ui calltreemanager.cpp configfrontend.cpp \ + cscopefrontend.cpp cscopemsgdlg.cpp cscopemsglayout.ui ctagsfrontend.cpp ctagslist.cpp \ + dirscanner.cpp dotfrontend.cpp dotparse.ypp dotscan.lpp editormanager.cpp \ + editorpage.cpp editortabs.cpp encoder.cpp filelist.cpp fileview.cpp fileviewlayout.ui \ + frontend.cpp graphedge.cpp graphnode.cpp graphprefdlg.cpp graphpreflayout.ui \ + graphwidget.cpp historypage.cpp historyview.cpp kscope.cpp kscopeactions.cpp \ + kscopeconfig.cpp kscopepixmaps.cpp main.cpp makedlg.cpp makefrontend.cpp makelayout.ui \ + newprojectdlg.cpp newprojectlayout.ui openprojectdlg.cpp openprojectlayout.ui \ + prefcolor.cpp prefcolorlayout.ui preferencesdlg.cpp preffont.cpp preffontlayout.ui \ + preffrontend.cpp preffrontendlayout.ui prefopt.cpp prefoptlayout.ui progressdlg.cpp \ + project.cpp projectbase.cpp projectfilesdlg.cpp projectfileslayout.ui \ + projectmanager.cpp querypage.cpp querypagebase.cpp queryresultsmenu.cpp queryview.cpp \ + queryviewdlg.cpp queryviewdriver.cpp queryviewlayout.ui querywidget.cpp \ + querywidgetlayout.ui scanprogressdlg.cpp scanprogresslayout.ui searchlist.cpp \ + searchresultsdlg.cpp searchresultslayout.ui symbolcompletion.cpp symboldlg.cpp \ + symbollayout.ui tabwidget.cpp treewidget.cpp welcomedlg.ui + +kscope_LDFLAGS = $(KDE_RPATH) $(all_libraries) +kscope_LDADD = -lkateinterfaces -lktexteditor $(LIB_KDEUI) + +# this is where the desktop file will go +shelldesktopdir = $(kde_appsdir)/Development +shelldesktop_DATA = kscope.desktop + +# this is where the shell's XML-GUI resource file goes +shellrcdir = $(kde_datadir)/kscope +shellrc_DATA = kscopeui.rc kscope_config + +picsdir = $(kde_datadir)/kscope/pics +pics_DATA = file_ro.png file_rw.png file_save.png query_locked.png \ + query_unlocked.png tab_list.png call_graph.png called_tree.png calling_tree.png \ + bookmark.png + +BUILT_SOURCES = dotparse.h +AM_YFLAGS = -d diff --git a/src/autocompletionlayout.ui b/src/autocompletionlayout.ui new file mode 100644 index 0000000..2c8c274 --- /dev/null +++ b/src/autocompletionlayout.ui @@ -0,0 +1,217 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>AutoCompletionLayout</class> +<widget class="QDialog"> + <property name="name"> + <cstring>AutoCompletionLayout</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>287</width> + <height>183</height> + </rect> + </property> + <property name="caption"> + <string>Auto-Completion Properties</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout20</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Minimum Characters</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer15</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>71</width> + <height>21</height> + </size> + </property> + </spacer> + <widget class="QSpinBox"> + <property name="name"> + <cstring>m_pMinCharsSpin</cstring> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout21</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Delay (ms)</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer16</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>101</width> + <height>21</height> + </size> + </property> + </spacer> + <widget class="QSpinBox"> + <property name="name"> + <cstring>m_pDelaySpin</cstring> + </property> + <property name="maxValue"> + <number>10000</number> + </property> + <property name="lineStep"> + <number>100</number> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout22</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Maximum Entries</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer17</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>81</width> + <height>21</height> + </size> + </property> + </spacer> + <widget class="QSpinBox"> + <property name="name"> + <cstring>m_pMaxEntriesSpin</cstring> + </property> + <property name="maxValue"> + <number>1000</number> + </property> + <property name="minValue"> + <number>1</number> + </property> + </widget> + </hbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer19</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>31</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout23</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer18</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>111</width> + <height>21</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pOKButton</cstring> + </property> + <property name="text"> + <string>OK</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pCancelButton</cstring> + </property> + <property name="text"> + <string>Cancel</string> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/src/bookmark.png b/src/bookmark.png Binary files differnew file mode 100644 index 0000000..5e76158 --- /dev/null +++ b/src/bookmark.png diff --git a/src/bookmarksdlg.cpp b/src/bookmarksdlg.cpp new file mode 100644 index 0000000..577476a --- /dev/null +++ b/src/bookmarksdlg.cpp @@ -0,0 +1,60 @@ +/*************************************************************************** + * + * Copyright (C) 2007 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include "bookmarksdlg.h" +#include "queryview.h" + +BookmarksDlg::BookmarksDlg(QWidget* pParent, const char* szName) : + BookmarksLayout (pParent, szName, true) +{ + // Do not show the "Function" column + m_pView->setColumnWidth(0, 0); + + // Handle requests for source locations + connect(m_pView, SIGNAL(lineRequested(const QString&, uint)), this, + SLOT(slotLineRequested(const QString&, uint))); +} + +BookmarksDlg::~BookmarksDlg() +{ +} + +void BookmarksDlg::getBookmark(QString& sPath, uint& nLine) +{ + sPath = m_sPath; + nLine = m_nLine; +} + +void BookmarksDlg::slotLineRequested(const QString& sPath, uint nLine) +{ + m_sPath = sPath; + m_nLine = nLine; + accept(); +} + +#include "bookmarksdlg.moc" + diff --git a/src/bookmarksdlg.h b/src/bookmarksdlg.h new file mode 100644 index 0000000..ab74f9d --- /dev/null +++ b/src/bookmarksdlg.h @@ -0,0 +1,53 @@ +/*************************************************************************** + * + * Copyright (C) 2007 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef BOOKMARKSDLG_H +#define BOOKMARKSDLG_H + +#include "bookmarkslayout.h" + +class BookmarksDlg : public BookmarksLayout +{ +Q_OBJECT + +public: + BookmarksDlg(QWidget* pParent = 0, const char* szName = 0); + ~BookmarksDlg(); + + QueryView* getView() { return m_pView; } + void getBookmark(QString&, uint&); + +private: + QString m_sPath; + uint m_nLine; + +private slots: + void slotLineRequested(const QString&, uint); +}; + +#endif + diff --git a/src/bookmarkslayout.ui b/src/bookmarkslayout.ui new file mode 100644 index 0000000..f1f8135 --- /dev/null +++ b/src/bookmarkslayout.ui @@ -0,0 +1,116 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>BookmarksLayout</class> +<widget class="QDialog"> + <property name="name"> + <cstring>BookmarksLayout</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>600</width> + <height>480</height> + </rect> + </property> + <property name="caption"> + <string>Global Bookmarks</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QueryView"> + <property name="name"> + <cstring>m_pView</cstring> + </property> + </widget> + <widget class="Line"> + <property name="name"> + <cstring>line1</cstring> + </property> + <property name="frameShape"> + <enum>HLine</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout1</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>291</width> + <height>21</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pCloseButton</cstring> + </property> + <property name="text"> + <string>Close</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<customwidgets> + <customwidget> + <class>QueryView</class> + <header location="local">queryview.h</header> + <sizehint> + <width>-1</width> + <height>-1</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>5</hordata> + <verdata>5</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + </customwidget> +</customwidgets> +<images> + <image name="image0"> + <data format="XBM.GZ" length="79">789c534e494dcbcc4b554829cdcdad8c2fcf4c29c95030e0524611cd48cd4ccf28010a1797249664262b2467241641a592324b8aa363156c15aab914146aadb90067111b1f</data> + </image> +</images> +<connections> + <connection> + <sender>m_pCloseButton</sender> + <signal>clicked()</signal> + <receiver>BookmarksLayout</receiver> + <slot>reject()</slot> + </connection> +</connections> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>queryview.h</includehint> +</includehints> +</UI> diff --git a/src/call_graph.png b/src/call_graph.png Binary files differnew file mode 100644 index 0000000..0b87ddd --- /dev/null +++ b/src/call_graph.png diff --git a/src/called_tree.png b/src/called_tree.png Binary files differnew file mode 100644 index 0000000..db58776 --- /dev/null +++ b/src/called_tree.png diff --git a/src/calling_tree.png b/src/calling_tree.png Binary files differnew file mode 100644 index 0000000..89e40d2 --- /dev/null +++ b/src/calling_tree.png diff --git a/src/calltreedlg.cpp b/src/calltreedlg.cpp new file mode 100644 index 0000000..65ee4f8 --- /dev/null +++ b/src/calltreedlg.cpp @@ -0,0 +1,336 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qfile.h> +#include <qtoolbutton.h> +#include <qbuttongroup.h> +#include <qwidgetstack.h> +#include <klocale.h> +#include <kfiledialog.h> +#include "calltreedlg.h" +#include "graphwidget.h" +#include "treewidget.h" +#include "kscopepixmaps.h" +#include "kscopeconfig.h" +#include "graphprefdlg.h" + +/** The currently supported version of saved call-tree files. */ +#define FILE_VERSION 5 + +/** Window flags for call-tree widgets. */ +#define CALL_TREE_W_FLAGS \ + WStyle_Customize | \ + WStyle_NormalBorder | \ + WStyle_Title | \ + WDestructiveClose + +/** File Name index for the file name generation */ +int CallTreeDlg::s_nFileNameIndex = 0; + +/** + * Class constructor. + * @param pParent The parent widget + * @param szName The widget's name + */ +CallTreeDlg::CallTreeDlg(QWidget* pParent, const char* szName) : + CallTreeLayout(pParent, szName, CALL_TREE_W_FLAGS) +{ + // Set button pixmaps + m_pCalledButton->setPixmap(GET_PIXMAP(CalledTree)); + m_pCallingButton->setPixmap(GET_PIXMAP(CallingTree)); + m_pGraphButton->setPixmap(GET_PIXMAP(CallGraph)); + m_pSaveButton->setPixmap(GET_PIXMAP(ButtonSaveAs)); + m_pZoomInButton->setPixmap(GET_PIXMAP(ButtonZoomIn)); + m_pZoomOutButton->setPixmap(GET_PIXMAP(ButtonZoomOut)); + m_pRotateButton->setPixmap(GET_PIXMAP(ButtonRotate)); + m_pPrefButton->setPixmap(GET_PIXMAP(ButtonPref)); + + // Open the location of a call + connect(m_pGraphWidget, SIGNAL(lineRequested(const QString&, uint)), + this, SIGNAL(lineRequested(const QString&, uint))); + connect(m_pCalledWidget, SIGNAL(lineRequested(const QString&, uint)), + this, SIGNAL(lineRequested(const QString&, uint))); + connect(m_pCallingWidget, SIGNAL(lineRequested(const QString&, uint)), + this, SIGNAL(lineRequested(const QString&, uint))); + + m_pCallingWidget->setMode(TreeWidget::Calling); + + // Get the default view from KScope's configuration + m_nDefView = Config().getDefGraphView(); +} + +/** + * Class destructor. + */ +CallTreeDlg::~CallTreeDlg() +{ +} + +/** + * @param sFunc The function to use as the root of the call tree + */ +void CallTreeDlg::setRoot(const QString& sFunc) +{ + m_sRoot = sFunc; + + // Generate unique file name to save call tree later + m_sFileName = sFunc; + m_sFileName.replace(' ', '_'); + m_sFileName += QString::number(++s_nFileNameIndex); + + // Set the root item in all views + m_pGraphWidget->setRoot(sFunc); + m_pCalledWidget->setRoot(sFunc); + m_pCallingWidget->setRoot(sFunc); +} + +/** + * Displays the dialogue. + */ +void CallTreeDlg::show() +{ + // Set the default view. + m_pViewGroup->setButton(m_nDefView); + m_pStack->raiseWidget(m_nDefView); + slotViewChanged(m_nDefView); + + CallTreeLayout::show(); +} + +/** + * Informs the call tree manager that this object should be removed from the + * list of call tree dialogues. + * The close event is received when the dialogue is explicitly closed by the + * user. This dialogue will not appear when the project is reopened, and it + * is therefore safe to delete the graph file at this point. + * @param pEvent Information on the closing event + */ +void CallTreeDlg::closeEvent(QCloseEvent* pEvent) +{ + if (!m_sFilePath.isEmpty()) + QFile::remove(m_sFilePath); + + emit closed(this); + QWidget::closeEvent(pEvent); +} + +extern void yyinit(CallTreeDlg*, FILE*, Encoder*); +extern int yyparse(); + +/** + * Restores a call tree from the given call tree file. + * NOTE: The call tree file is deleted when loading is complete. + * @param sProjPath The full path of the project directory + * @param sFileName The name of the call tree file to load + * @return true if successful, false otherwise + */ +bool CallTreeDlg::load(const QString& sProjPath, const QString& sFileName) +{ + QString sPath; + FILE* pFile; + int nVersion, nView, nResult; + Encoder enc; + + // Create the full path name + sPath = sProjPath + "/" + sFileName; + + // Open the file for reading + pFile = fopen(sPath.latin1(), "r"); + if (pFile == NULL) + return false; + + // Check file version + if ((fscanf(pFile, "VERSION=%d\n", &nVersion) != 1) || + (nVersion != FILE_VERSION)) { + fclose(pFile); + return false; + } + + // Get default view + if ((fscanf(pFile, "View=%d\n", &nView) == 1) && + (nView >= 0) && + (nView <= 2)) { + m_nDefView = nView; + } + + // Read the call trees and the graph stored on this file + yyinit(this, pFile, &enc); + nResult = yyparse(); + + // Close the file + fclose(pFile); + + // Check the result returned by the parser + if (nResult != 0) + return false; + + // Store the file name + m_sFileName = sFileName; + m_sFilePath = sPath; + + // Draw the graph + m_pGraphWidget->draw(); + return true; +} + +/** + * Writes the contents of the call tree dialog to a call tree file. + * This method is called for call trees before the owner project is + * closed. + * @param sProjPath The full path of the project directory + */ +void CallTreeDlg::store(const QString& sProjPath) +{ + QString sPath; + FILE* pFile; + + // Create the full file path + sPath = sProjPath + "/" + m_sFileName; + m_sFilePath = sPath; + + // Open a file for writing (create if necessary) + pFile = fopen(sPath.latin1(), "w+"); + if (pFile == NULL) + return; + + // Write header + fprintf(pFile, "VERSION=%d\n", FILE_VERSION); + fprintf(pFile, "View=%d\n", m_pViewGroup->selectedId()); + + // Save the contents of all widgets + m_pCalledWidget->save(pFile); + m_pCallingWidget->save(pFile); + m_pGraphWidget->save(pFile); + + // Close the file + fclose(pFile); +} + +/** + * Saves the graph to a dot file. + * The user is prompted for a name to use for the file, and then graph + * widget writes its information to this file (using the dot language). + * This slot is connected to the clicked() signal of the "Save As..." button. + */ +void CallTreeDlg::slotSaveClicked() +{ + QString sFile; + + // Prompt the user for a file name + sFile = KFileDialog::getSaveFileName(":kscope"); + + // Save the graph to a file (unless the user did not give a file name) + if (!sFile.isEmpty()) + m_pGraphWidget->save(sFile); +} + +/** + * Increases the zoom factor of the graph. + * This slot is connected to the clicked() signal of the "Zoom In" button. + */ +void CallTreeDlg::slotZoomInClicked() +{ + m_pGraphWidget->zoom(true); + m_pGraphWidget->draw(); +} + +/** + * Decreases the zoom factor of the graph. + * This slot is connected to the clicked() signal of the "Zoom Out" button. + */ +void CallTreeDlg::slotZoomOutClicked() +{ + m_pGraphWidget->zoom(false); + m_pGraphWidget->draw(); +} + +/** + * Changes the graph's layout direction. + * This slot is connected to the clicked() signal of the "Rotate" button. + */ +void CallTreeDlg::slotRotateClicked() +{ + m_pGraphWidget->rotate(); + m_pGraphWidget->draw(); +} + +/** + * Opens the call graph preferences dialogue. + * This slot is connected to the clicked() signal of the "Preferences" button. + */ +void CallTreeDlg::slotPrefClicked() +{ + GraphPrefDlg dlg(this); + int nMaxNodeDegree; + + if (dlg.exec() == QDialog::Accepted) { + nMaxNodeDegree = dlg.getMaxNodeDegree(); + Config().setGraphMaxNodeDegree(nMaxNodeDegree); + m_pGraphWidget->setMaxNodeDegree(nMaxNodeDegree); + } +} + +/** + * Prepares the selected view. + * This slot is called when the user chooses a different view through the + * toggle buttons in the dialogue's toolbar. + * @param nView Identifies the selected view + */ +void CallTreeDlg::slotViewChanged(int nView) +{ + switch (nView) { + case 0: + // Call graph + setCaption(i18n("Call Graph")); + m_pGraphGroup->setEnabled(true); + m_pHelpLabel->setText(i18n("Right-click a function node or an arrow " + "head for more options.")); + break; + + case 1: + // Called functions tree + setCaption(i18n("Called Functions Tree")); + m_pGraphGroup->setEnabled(false); + m_pHelpLabel->setText(i18n("Right-click a tree item for more " + "options.")); + m_pCalledWidget->queryRoot(); + break; + + case 2: + // Calling functions tree + setCaption(i18n("Calling Functions Tree")); + m_pGraphGroup->setEnabled(false); + m_pHelpLabel->setText(i18n("Right-click a tree item for more " + "options.")); + m_pCallingWidget->queryRoot(); + break; + } + + Config().setDefGraphView(nView); +} + +#include "calltreedlg.moc" diff --git a/src/calltreedlg.h b/src/calltreedlg.h new file mode 100644 index 0000000..d5f567b --- /dev/null +++ b/src/calltreedlg.h @@ -0,0 +1,111 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef CALLTREEDLG_H +#define CALLTREEDLG_H + +#include <qwidget.h> +#include <qlistview.h> +#include <calltreelayout.h> + +/** + * A multiple-view window showing function call information. + * The available views are: + * - Call graph, showing both calling and call functions + * - Called functions tree + * - Calling functions tree + * NOTE: This is class is now derived from QWidget instead of QDialog. This + * means that call-trees are independent windows, which can be maximised or + * minimised. + * @author Elad Lahav + */ +class CallTreeDlg : public CallTreeLayout +{ + Q_OBJECT + +public: + CallTreeDlg(QWidget* pParent = 0, const char* szName = 0); + ~CallTreeDlg(); + + void setRoot(const QString&); + bool load(const QString&, const QString&); + void store(const QString&); + + /** Returns Call Tree filename */ + QString getFileName() { return m_sFileName; } + +public slots: + virtual void show(); + +signals: + /** + * Emitted when the user makes a request to view the contents of a + * location in the source code. + * This can be the location of a call, the definition of a function, + * etc. + * @param sPath The full path of the file to show + * @param nLine The line number in this file + */ + void lineRequested(const QString& sPath, uint nLine); + + /** + * Emitted when the user closes the tree view. + */ + void closed(const CallTreeDlg*); + +protected: + virtual void closeEvent(QCloseEvent*); + +protected slots: + virtual void slotSaveClicked(); + virtual void slotZoomInClicked(); + virtual void slotZoomOutClicked(); + virtual void slotRotateClicked(); + virtual void slotPrefClicked(); + virtual void slotViewChanged(int); + +private: + /** The root function. */ + QString m_sRoot; + + /** A unique file name used for storing the call tree on a file. + The name is a combination of the root function and an incremented + index. */ + QString m_sFileName; + + /** The full path of the file on which the call tree was saved + (empty if this graph was never stored). */ + QString m_sFilePath; + + /** The view to show when the dialogue is first displayed. */ + int m_nDefView; + + /** An index for the generating unique file names. */ + static int s_nFileNameIndex; +}; + +#endif diff --git a/src/calltreelayout.ui b/src/calltreelayout.ui new file mode 100644 index 0000000..2562977 --- /dev/null +++ b/src/calltreelayout.ui @@ -0,0 +1,430 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>CallTreeLayout</class> +<widget class="QWidget"> + <property name="name"> + <cstring>CallTreeLayout</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>695</width> + <height>578</height> + </rect> + </property> + <property name="caption"> + <string>Call Graph</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout2</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>m_pViewGroup</cstring> + </property> + <property name="frameShape"> + <enum>NoFrame</enum> + </property> + <property name="lineWidth"> + <number>0</number> + </property> + <property name="title"> + <string></string> + </property> + <property name="exclusive"> + <bool>true</bool> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QToolButton"> + <property name="name"> + <cstring>m_pGraphButton</cstring> + </property> + <property name="text"> + <string>...</string> + </property> + <property name="toggleButton"> + <bool>true</bool> + </property> + <property name="autoRaise"> + <bool>true</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Call Graph</string> + </property> + </widget> + <widget class="QToolButton"> + <property name="name"> + <cstring>m_pCalledButton</cstring> + </property> + <property name="text"> + <string>...</string> + </property> + <property name="toggleButton"> + <bool>true</bool> + </property> + <property name="autoRaise"> + <bool>true</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Called Functions Tree</string> + </property> + </widget> + <widget class="QToolButton"> + <property name="name"> + <cstring>m_pCallingButton</cstring> + </property> + <property name="text"> + <string>...</string> + </property> + <property name="toggleButton"> + <bool>true</bool> + </property> + <property name="autoRaise"> + <bool>true</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Calling Functions Tree</string> + </property> + </widget> + </hbox> + </widget> + <widget class="Line"> + <property name="name"> + <cstring>line1</cstring> + </property> + <property name="frameShape"> + <enum>VLine</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + </widget> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>m_pGraphGroup</cstring> + </property> + <property name="frameShape"> + <enum>NoFrame</enum> + </property> + <property name="lineWidth"> + <number>0</number> + </property> + <property name="title"> + <string></string> + </property> + <property name="flat"> + <bool>true</bool> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QToolButton"> + <property name="name"> + <cstring>m_pSaveButton</cstring> + </property> + <property name="text"> + <string>a</string> + </property> + <property name="autoRaise"> + <bool>true</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Save As...</string> + </property> + </widget> + <widget class="QToolButton"> + <property name="name"> + <cstring>m_pZoomInButton</cstring> + </property> + <property name="text"> + <string>a</string> + </property> + <property name="toggleButton"> + <bool>false</bool> + </property> + <property name="autoRaise"> + <bool>true</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Zoom In</string> + </property> + </widget> + <widget class="QToolButton"> + <property name="name"> + <cstring>m_pZoomOutButton</cstring> + </property> + <property name="text"> + <string>a</string> + </property> + <property name="toggleButton"> + <bool>false</bool> + </property> + <property name="autoRaise"> + <bool>true</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Zoom Out</string> + </property> + </widget> + <widget class="QToolButton"> + <property name="name"> + <cstring>m_pRotateButton</cstring> + </property> + <property name="text"> + <string>a</string> + </property> + <property name="toggleButton"> + <bool>false</bool> + </property> + <property name="autoRaise"> + <bool>true</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Rotate</string> + </property> + </widget> + <widget class="QToolButton"> + <property name="name"> + <cstring>m_pPrefButton</cstring> + </property> + <property name="text"> + <string>a</string> + </property> + <property name="toggleButton"> + <bool>false</bool> + </property> + <property name="autoRaise"> + <bool>true</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Preferences</string> + </property> + </widget> + </hbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>110</width> + <height>21</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QWidgetStack"> + <property name="name"> + <cstring>m_pStack</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>7</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>WStackPage</cstring> + </property> + <attribute name="id"> + <number>0</number> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="GraphWidget"> + <property name="name"> + <cstring>m_pGraphWidget</cstring> + </property> + </widget> + </vbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>WStackPage</cstring> + </property> + <attribute name="id"> + <number>1</number> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="TreeWidget"> + <property name="name"> + <cstring>m_pCalledWidget</cstring> + </property> + </widget> + </vbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>WStackPage</cstring> + </property> + <attribute name="id"> + <number>2</number> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="TreeWidget"> + <property name="name"> + <cstring>m_pCallingWidget</cstring> + </property> + </widget> + </vbox> + </widget> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>m_pHelpLabel</cstring> + </property> + <property name="text"> + <string>Help Message</string> + </property> + </widget> + </vbox> +</widget> +<customwidgets> + <customwidget> + <class>GraphWidget</class> + <header location="local">graphwidget.h</header> + <sizehint> + <width>-1</width> + <height>-1</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>7</hordata> + <verdata>7</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + </customwidget> + <customwidget> + <class>TreeWidget</class> + <header location="local">treewidget.h</header> + <sizehint> + <width>-1</width> + <height>-1</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>7</hordata> + <verdata>7</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + </customwidget> +</customwidgets> +<images> + <image name="image0"> + <data format="PNG" length="1003">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b000003b249444154388db5944d4c5c5518869f73ce9db9cc40f929cc30300e18129a50685268a28291982e1a2175212eaab1c49d3f8971e7aec6aedb54a32b435dd8685cb8c0c4b8b0feb421b7bd8186a069d23209144b18cb0c02f3732ff3c3ccbdd705a571941213f1dd9d93739ef37e6fbeef08d334d9d5d0d090c701c8344d2176c1a6697a5d5d5dd8b64d2a95c2b2ac7f05b12c8b783c8e6118d8b6fd685fdb753a39394928e2a7c55724d693a7e214104220242805520a84d8b9e4790f0b1302c7751958f1e30534be9fa41a0c60db362dbe227f64af91b6e7719c2dfc0117a71c603da591de2882a7d3d4ec27122b10acad50aeb88044d542b84de3af7ab44aa55274f4e449dbf354bc347aa096fbf37e7efa2ecbad1b36907d78b289de8120232f37726250c3f52cf0a026a0f6065b9645d929e038167aa096d91b307171059034b5f8e9e9eb44fa052b4b25eeccd9dc99cb71f6ad082fbea2e351c075bdbdc13b9109fc01b83fef63e2e232e03076b68393231a0dcd024daf502c1e66e67a89cb971ef0e5a74bb4b61e6378248094b92ab0ac024b70ca017ef8360394187b2dc6d8b89ffa500e4d4f93d9083275d5e6f9913ade3dd70ee87c7d25c95646a25415aadab152b09ed4f8c5cc130a853839eac3951b684a61e70ef3f9c739668c24eba912afbfddc6d4d506e6a6d7b8b7d082aa8eb8dab19482cdcd220e36dd3dc19df23545de0a71f952861923493456cfd3cf36123c54a0b7bf0e8064621b21f7712c040857071ca4eea0f40a99f510573ec971cb48030e811a1faded3e94be85f2fb00703d1ff26f96ab9f011a5b04d0c4f26fdb948a754c1b25668c2491483d5d5d611617d6b8f0fe32cb779b492ce65168b4c774a4701eefd8f3a02d56e6e8f13aeefe9a63fa5a89d1970e91d908f3d46003e1480d173e28b37827c9b977348ab92ce16890237d1ec907ee3e8e3d8f60d0e1f4583d009f7d9860662acff89bad1c1faad0717493f7ce7713ed8cb0995c255f2a317a26427b67198f6a70f51c0a41b902279ed1187f23cc17130b7c74de65eac77a7a8f05517e41e2deefa4930576db60fa7a8ee1535134df3e93e7b82e0817476439fd6a9070a49f6fbe4a3077739db99b15c001146dd13a46cf1cc1f83943c62e51de76b0738fc9381e8f3390f0236b252e124999e11724fd833196164bacadb87848224fe874f779b43fe9f2dca928956d505a96dbb3f9bdc18661e00534c26d1a7a40e13912a11c9454280da41048a9915c75585df500074d53d816dc9edd229528ef0db66dbbea3ffdaffa471f1f28d8344df1bf800f1a6e9aa6f813c39885bc050f269c0000000049454e44ae426082</data> + </image> +</images> +<connections> + <connection> + <sender>m_pPrefButton</sender> + <signal>clicked()</signal> + <receiver>CallTreeLayout</receiver> + <slot>slotPrefClicked()</slot> + </connection> + <connection> + <sender>m_pRotateButton</sender> + <signal>clicked()</signal> + <receiver>CallTreeLayout</receiver> + <slot>slotRotateClicked()</slot> + </connection> + <connection> + <sender>m_pZoomOutButton</sender> + <signal>clicked()</signal> + <receiver>CallTreeLayout</receiver> + <slot>slotZoomOutClicked()</slot> + </connection> + <connection> + <sender>m_pZoomInButton</sender> + <signal>clicked()</signal> + <receiver>CallTreeLayout</receiver> + <slot>slotZoomInClicked()</slot> + </connection> + <connection> + <sender>m_pSaveButton</sender> + <signal>clicked()</signal> + <receiver>CallTreeLayout</receiver> + <slot>slotSaveClicked()</slot> + </connection> + <connection> + <sender>m_pViewGroup</sender> + <signal>clicked(int)</signal> + <receiver>CallTreeLayout</receiver> + <slot>slotViewChanged(int)</slot> + </connection> + <connection> + <sender>m_pViewGroup</sender> + <signal>clicked(int)</signal> + <receiver>m_pStack</receiver> + <slot>raiseWidget(int)</slot> + </connection> +</connections> +<slots> + <slot access="protected">slotSaveClicked()</slot> + <slot access="protected">slotZoomInClicked()</slot> + <slot access="protected">slotZoomOutClicked()</slot> + <slot access="protected">slotRotateClicked()</slot> + <slot access="protected">slotViewChanged(int)</slot> + <slot access="protected">slotViewChanged(QWidget*)</slot> + <slot access="protected">slotPrefClicked()</slot> +</slots> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>graphwidget.h</includehint> + <includehint>treewidget.h</includehint> + <includehint>treewidget.h</includehint> +</includehints> +</UI> diff --git a/src/calltreemanager.cpp b/src/calltreemanager.cpp new file mode 100644 index 0000000..5dc8e9f --- /dev/null +++ b/src/calltreemanager.cpp @@ -0,0 +1,136 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include "calltreemanager.h" +#include "calltreedlg.h" +#include "projectmanager.h" + +/** + * Class constructor. + * @param pParent The widget to use as the parent of all Call Tree + * dialogues + */ +CallTreeManager::CallTreeManager(QWidget* pParent) : QObject(pParent) +{ + // Delete dialogue objects when they are removed from the list + m_lstDialogs.setAutoDelete(true); +} + +/** + * Class destructor. + */ +CallTreeManager::~CallTreeManager() +{ +} + +/** + * Saves all call trees into the project directory. + * @param sProjPath The project's directory + * @param slFiles Holds a list of saved file names, upon return + */ +void CallTreeManager::saveOpenDialogs(const QString& sProjPath, + QStringList& slFiles) +{ + CallTreeDlg *pDlg; + + // Iterate over the open dialogues + for (pDlg = m_lstDialogs.first(); pDlg != NULL; + pDlg = m_lstDialogs.next()) { + pDlg->store(sProjPath); + slFiles += pDlg->getFileName(); + } +} + +/** + * Loads all call trees according to the list of files + * @param sProjPath The project's directory + * @param slFiles A list of file names to open + */ +void CallTreeManager::loadOpenDialogs(const QString& sProjPath, + const QStringList& slFiles) +{ + QStringList::ConstIterator itr; + CallTreeDlg *pDlg; + + for (itr = slFiles.begin(); itr != slFiles.end(); ++itr) { + // Create a new dialogue for this file + pDlg = addDialog(); + + // Try to load the graph from the file + if (!pDlg->load(sProjPath, *itr)) { + m_lstDialogs.remove(pDlg); + continue; + } + + // Show the call tree + pDlg->show(); + } +} + +/** + * Creates a new Call Tree dialogue. + * @return The newly allocated dialogue object + */ +CallTreeDlg* CallTreeManager::addDialog() +{ + CallTreeDlg* pDlg; + + // Create a modeless call tree dialogue + pDlg = new CallTreeDlg(); + m_lstDialogs.append(pDlg); + + // Open an editor whenever a function name is double-clicked + connect(pDlg, SIGNAL(lineRequested(const QString&, uint)), + this, SIGNAL(lineRequested(const QString&, uint))); + + // Track the closing of the call tree dialog + connect(pDlg, SIGNAL(closed(const CallTreeDlg*)), this, + SLOT(slotRemoveDialog(const CallTreeDlg*))); + + return pDlg; +} + +/** + * Closes all Call Tree dialogues. + */ +void CallTreeManager::closeAll() +{ + m_lstDialogs.clear(); +} + +/** + * Removes a Call Tree dialogue from the list of open Call Trees. + * This slot is connected to the closed() signal emitted by the dialogue. + * @param pDlg The dialogue to remove from the list + */ +void CallTreeManager::slotRemoveDialog(const CallTreeDlg* pDlg) +{ + m_lstDialogs.remove(pDlg); +} + +#include "calltreemanager.moc" + diff --git a/src/calltreemanager.h b/src/calltreemanager.h new file mode 100644 index 0000000..adb91e6 --- /dev/null +++ b/src/calltreemanager.h @@ -0,0 +1,71 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef CALLTREEMANAGER_H +#define CALLTREEMANAGER_H + +#include <qwidget.h> +#include <qptrlist.h> + +class CallTreeDlg; + +/** + * Manages all call tree dialogs within the project. + * Responsible for saving/loading of the call tree dialogs. + * @author Albert Yosher + */ +class CallTreeManager : public QObject +{ + Q_OBJECT + +public: + CallTreeManager(QWidget*); + ~CallTreeManager(); + + void saveOpenDialogs(const QString&, QStringList&); + void loadOpenDialogs(const QString&, const QStringList&); + CallTreeDlg* addDialog(); + void closeAll(); + +signals: + /** + * Emitted when any call tree dialogue sends a request to view a location + * in the source code. + * @param sPath The full path of the file to show + * @param nLine The line number in this file + */ + void lineRequested(const QString& sPath, uint nLine); + +private: + /** The list of open call tree dialogues. */ + QPtrList<CallTreeDlg> m_lstDialogs; + +private slots: + void slotRemoveDialog(const CallTreeDlg*); +}; + +#endif diff --git a/src/configfrontend.cpp b/src/configfrontend.cpp new file mode 100644 index 0000000..27e4e32 --- /dev/null +++ b/src/configfrontend.cpp @@ -0,0 +1,172 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <kstandarddirs.h> +#include "configfrontend.h" + +/** + * Class constructor. + * @param bAutoDelete True to destroy the object when the process ends, + * false otherwise + */ +ConfigFrontend::ConfigFrontend(bool bAutoDelete) : + Frontend(1, bAutoDelete) +{ +} + +/** + * Class destructor. + */ +ConfigFrontend::~ConfigFrontend() +{ +} + +/** + * Executes the script using the "sh" shell. + * @param sCscopePath If given, overrides the automatic check for Cscope's + * path + * @param sCtagsPath If given, overrides the automatic check for Ctags' + * path + * @param sDotPath If given, overrides the automatic check for Dot's + * path + * @param bCscopeOptsOnly Only verify cscope's path and options + * @return true if successful, false otherwise + */ +bool ConfigFrontend::run(const QString& sCscopePath, + const QString& sCtagsPath, const QString& sDotPath, + bool bCscopeOptsOnly) +{ + QStringList slArgs; + KStandardDirs sd; + QString sScript; + + // Execute using the user's shell + setUseShell(true); + + // Find the configuration script + sScript = sd.findResource("data", "kscope/kscope_config"); + if (sScript.isEmpty()) + return false; + + // Set command line arguments + slArgs.append("sh"); + slArgs.append(sScript); + + if (bCscopeOptsOnly) + slArgs.append("-co"); + + // Initialise environment + setEnvironment("CSCOPE_PATH", sCscopePath); + setEnvironment("CTAGS_PATH", sCtagsPath); + setEnvironment("DOT_PATH", sDotPath); + + // Parser initialisation + m_delim = Newline; + m_nNextResult = CscopePath; + + if (!Frontend::run("sh", slArgs)) + return false; + + emit test(CscopePath); + return true; +} + +/** + * Handles tokens generated by the script. + * Each token represents a line in the script's output, and is the result of + * a different test. + * @param sToken The generated token + */ +Frontend::ParseResult ConfigFrontend::parseStdout(QString& sToken, + ParserDelim) +{ + uint nResult; + + // Store the type of test for which the given token in the result + nResult = m_nNextResult; + + // Determine the next test + switch (m_nNextResult) { + case CscopePath: + if (sToken == "ERROR") + m_nNextResult = CtagsPath; + else + m_nNextResult = CscopeVersion; + break; + + case CscopeVersion: + if (sToken == "ERROR") + m_nNextResult = CtagsPath; + else + m_nNextResult = CscopeVerbose; + break; + + case CscopeVerbose: + m_nNextResult = CscopeSlowPath; + break; + + case CscopeSlowPath: + m_nNextResult = CtagsPath; + break; + + case CtagsPath: + if (sToken == "ERROR") + m_nNextResult = END; + else + m_nNextResult = CtagsExub; + break; + + case CtagsExub: + if (sToken == "ERROR") + m_nNextResult = END; + else + m_nNextResult = DotPath; + break; + + case DotPath: + if (sToken == "ERROR") + m_nNextResult = END; + else + m_nNextResult = DotPlain; + break; + + case DotPlain: + m_nNextResult = END; + break; + + case END: + return DiscardToken; + } + + // Publish the result and the type of the next test + emit result(nResult, sToken); + emit test(m_nNextResult); + + return DiscardToken; +} + +#include "configfrontend.moc" diff --git a/src/configfrontend.h b/src/configfrontend.h new file mode 100644 index 0000000..df5e303 --- /dev/null +++ b/src/configfrontend.h @@ -0,0 +1,77 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef CONFIGFRONTEND_H +#define CONFIGFRONTEND_H + +#include <frontend.h> + +/** + * Frontend to the kscope_config shell script. + * The script executes a set of tests and outputs their results. + * @author Elad Lahav + */ +class ConfigFrontend : public Frontend +{ + Q_OBJECT + +public: + ConfigFrontend(bool bAutoDelete = false); + ~ConfigFrontend(); + + bool run(const QString&, const QString&, const QString&, + bool bCscopeOptsOnly = false); + + /** + * The types of tests executed by the script. + */ + enum { CscopePath, CscopeVersion, CscopeVerbose, CscopeSlowPath, + CtagsPath, CtagsExub, DotPath, DotPlain, END }; + +signals: + /** + * Indicates that the script is now running a given test. + * @param nType The type of test being executed + */ + void test(uint nType); + + /** + * Called after a test has produced a result. + * @param nType The type of test executed + * @param sResult The obtained result + */ + void result(uint nType, const QString& sResult); + +protected: + virtual ParseResult parseStdout(QString&, ParserDelim); + +private: + /** The type of test whose result is expected next. */ + uint m_nNextResult; +}; + +#endif diff --git a/src/cscopefrontend.cpp b/src/cscopefrontend.cpp new file mode 100644 index 0000000..7c8f288 --- /dev/null +++ b/src/cscopefrontend.cpp @@ -0,0 +1,524 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qfileinfo.h> +#include <qtimer.h> +#include <kconfig.h> +#include <kmessagebox.h> +#include <klocale.h> +#include <kglobalsettings.h> +#include "cscopefrontend.h" +#include "kscopeconfig.h" +#include "configfrontend.h" + +#define BUILD_STR "Building symbol database %d of %d" +#define SEARCH_STR "Search %d of %d" +#define INV_STR "Possible references retrieved %d of %d" +#define REGEXP_STR "Symbols matched %d of %d" +#define SEARCHEND_STR "%d lines" + +QString CscopeFrontend::s_sProjPath; +uint CscopeFrontend::s_nProjArgs; +uint CscopeFrontend::s_nSupArgs; + +/** + * Class constructor. + * @param bAutoDelete true to delete the object once the process has + * terminated, false otherwise + */ +CscopeFrontend::CscopeFrontend(bool bAutoDelete) : + Frontend(CSCOPE_RECORD_SIZE, bAutoDelete), + m_state(Unknown), + m_sErrMsg(""), + m_bRebuildOnExit(false) +{ +} + +/** + * Class destructor. + */ +CscopeFrontend::~CscopeFrontend() +{ +} + +/** + * Executes a Cscope process using the given command line arguments. + * The full path to the Cscope executable should be set in the "Path" key + * under the "Cscope" group. + * @param slArgs Command line arguments for Cscope + * @return true if successful, false otherwise + */ +bool CscopeFrontend::run(const QStringList& slArgs) +{ + QStringList slCmdLine; + + // Set the command line arguments + slCmdLine.append(Config().getCscopePath()); + slCmdLine += slArgs; + + // Use verbose mode, if supported + if (s_nSupArgs & VerboseOut) + slCmdLine << "-v"; + + // Project-specific options + if (s_nProjArgs & Kernel) + slCmdLine << "-k"; + if (s_nProjArgs & InvIndex) + slCmdLine << "-q"; + if (s_nProjArgs & NoCompression) + slCmdLine << "-c"; + if (s_nProjArgs & s_nSupArgs & SlowPathDef) + slCmdLine << "-D"; + + // Run a new process + if (!Frontend::run("cscope", slCmdLine, s_sProjPath)) { + emit aborted(); + return false; + } + + return true; +} + +/** + * Executes a Cscope query. + * A query is composed of a numeric type and a query text, which are written + * to the stndard input of the currently running Cscope process. + * @param nType The type of query to run + * @param sText The query's text + * @param bCase true for case-sensitive queries, false otherwise + * @param nMaxRecords The maximal number of records to return (abort if this + * number is exceeded) + */ +void CscopeFrontend::query(uint nType, const QString& sText, bool bCase, + uint nMaxRecords) +{ + QString sQuery; + QStringList slArgs; + + m_nMaxRecords = nMaxRecords; + + // Create the Cscope command line + slArgs.append(QString("-L") + QString::number(nType)); + slArgs.append(sText); + slArgs.append("-d"); + if (!bCase) + slArgs.append("-C"); + + run(slArgs); + + // Initialise stdout parsing + m_state = SearchSymbol; + m_delim = WSpace; + + emit progress(0, 1); +} + +/** + * Rebuilds the symbol database of the current project. + */ +void CscopeFrontend::rebuild() +{ + QStringList slArgs; + + // If a process is already running, kill it start a new one + if (isRunning()) { + m_bRebuildOnExit = true; + kill(); + return; + } + + // Run the database building process + slArgs.append("-b"); + run(slArgs); + + // Initialise output parsing + m_state = BuildStart; + m_delim = Newline; + + emit progress(0, 1); +} + +/** + * Sets default parameters for all CscopeFrontend projects based on the + * current project. + * @param sProjPath The full path of the project's directory + * @param nArgs Project-specific command-line arguments + */ +void CscopeFrontend::init(const QString& sProjPath, uint nArgs) +{ + s_sProjPath = sProjPath; + s_nProjArgs = nArgs; +} + +/** + * Stops a Cscope action. + */ +void CscopeFrontend::slotCancel() +{ + kill(); +} + +/** + * Parses the output of a Cscope process. + * Implements a state machine, where states correspond to the output of the + * controlled Cscope process. + * @param sToken The current token read (the token delimiter is determined + * by the current state) + * @return A value indicating the way this token should be treated: dropped, + * added to the token queue, or finishes a new record + */ +Frontend::ParseResult CscopeFrontend::parseStdout(QString& sToken, + ParserDelim /* ignored */) +{ + int nFiles, nTotal, nRecords; + ParseResult result = DiscardToken; + ParserState stPrev; + + // Remember previous state + stPrev = m_state; + + // Handle the token according to the current state + switch (m_state) { + case BuildStart: + if (sToken == "Building cross-reference...") { + m_state = BuildSymbol; + m_delim = WSpace; + } + else if (sToken == "Building inverted index...") { + emit buildInvIndex(); + } + + result = DiscardToken; + break; + + case BuildSymbol: + // A single angle bracket is the prefix of a progress indication, + // while double brackets is Cscope's prompt for a new query + if (sToken == ">") { + m_state = Building; + m_delim = Newline; + } + + result = DiscardToken; + break; + + case Building: + // Try to get building progress + if (sscanf(sToken.latin1(), BUILD_STR, &nFiles, &nTotal) == 2) { + emit progress(nFiles, nTotal); + + // Check for last progress message + if (nFiles == nTotal) { + m_state = BuildStart; + m_delim = Newline; + + result = DiscardToken; + break; + } + } + + // Wait for another progress line or the "ready" symbol + m_state = BuildSymbol; + m_delim = WSpace; + + result = DiscardToken; + break; + + case SearchSymbol: + // Check for more search progress, or the end of the search, + // designated by a line in the format of "cscope: X lines" + if (sToken == ">") { + m_state = Searching; + m_delim = Newline; + result = DiscardToken; + break; + } + else if (sToken == "cscope:") { + m_state = SearchEnd; + m_delim = Newline; + result = DiscardToken; + break; + } + + case File: + // Is this the first entry? If so, signal that the query is complete + if (stPrev != LineText) + emit progress(1, 1); + + // Treat the token as the name of the file in this record + m_state = Func; + result = AcceptToken; + break; + + case Searching: + // Try to get the search progress value (ignore other messages) + if ((sscanf(sToken.latin1(), SEARCH_STR, &nFiles, &nTotal) == 2) || + (sscanf(sToken.latin1(), INV_STR, &nFiles, &nTotal) == 2) || + (sscanf(sToken.latin1(), REGEXP_STR, &nFiles, &nTotal) == 2)) { + emit progress(nFiles, nTotal); + } + + m_state = SearchSymbol; + m_delim = WSpace; + result = DiscardToken; + break; + + case SearchEnd: + // Get the number of results found in this search + if ((sscanf(sToken.latin1(), SEARCHEND_STR, &nRecords) == 1) && + (m_nMaxRecords > 0) && + (nRecords > m_nMaxRecords)) { + result = Abort; + } + else { + m_state = File; + m_delim = WSpace; + result = DiscardToken; + } + + break; + + case Func: + // Treat the token as the name of the function in this record + if (sToken.toInt()) { + // In case of a global definition, there is no function name, and + // instead the line number is given immediately + m_state = LineText; + m_delim = Newline; + } + else { + // Not a number, it is the name of the function + m_state = Line; + } + + result = AcceptToken; + break; + + case Line: + // Treat the token as the line number in this record + m_state = LineText; + m_delim = Newline; + result = AcceptToken; + break; + + case LineText: + // Treat the token as the text of this record, and report a new + // record + m_state = File; + m_delim = WSpace; + result = RecordReady; + break; + + default: + // Do nothing (prevents a compilation warning for unused enum values) + break; + } + + return result; +} + +/** + * Handles Cscope messages sent to the standard error stream. + * @param sText The error message text + */ +void CscopeFrontend::parseStderr(const QString& sText) +{ + // Wait for a complete line to arrive + m_sErrMsg += sText; + if (!sText.endsWith("\n")) + return; + + // Display the error message + emit error(m_sErrMsg); + + // Line displayed, reset the text accumulator + m_sErrMsg = ""; +} + +/** + * Called when the underlying process exits. + * Checks if the rebuild flag was raised, and if so restarts the building + * process. + */ +void CscopeFrontend::finalize() +{ + // Reset the parser state machine + m_state = Unknown; + + // Restart the building process, if required + if (m_bRebuildOnExit) { + m_bRebuildOnExit = false; + rebuild(); + } +} + +/** + * Class constructor. + * @param pMainWidget The parent widget to use for the progress bar and + * label + */ +CscopeProgress::CscopeProgress(QWidget* pMainWidget) : QObject(), + m_pMainWidget(pMainWidget), + m_pProgressBar(NULL), + m_pLabel(NULL) +{ +} + +/** + * Class destructor. + */ +CscopeProgress::~CscopeProgress() +{ +} + +/** + * Displays query progress information. + * If the progress value is below the expected final value, a progress bar is + * used to show the advance of the query process. Otherwise, a label is + * displayed asking the user to wait ahile the query output is processed. + * @param nProgress The current progress value + * @param nTotal The expected final value + */ +void CscopeProgress::setProgress(int nProgress, int nTotal) +{ + // Was the final value is reached? + if (nProgress == nTotal) { + // Destroy the progress bar + if (m_pProgressBar != NULL) { + delete m_pProgressBar; + m_pProgressBar = NULL; + } + + // Show the "Please wait..." label + if (m_pLabel == NULL) { + m_pLabel = new QLabel(i18n("Processing query results, " + "please wait..."), m_pMainWidget); + m_pLabel->setFrameStyle(QFrame::Box | QFrame::Plain); + m_pLabel->setLineWidth(1); + m_pLabel->adjustSize(); + + m_pLabel->setPaletteBackgroundColor( + KGlobalSettings::highlightColor()); + m_pLabel->setPaletteForegroundColor( + KGlobalSettings::highlightedTextColor()); + + QTimer::singleShot(1000, this, SLOT(slotShowLabel())); + } + + return; + } + + // Create the progress bar, if it does not exist. + // Note that the progress bar will only be displayed one second after the + // first progress signal is received. Thus the bar will not be displayed + // on very short queries. + if (m_pProgressBar == NULL) { + m_pProgressBar = new QProgressBar(m_pMainWidget); + QTimer::singleShot(1000, this, SLOT(slotShowProgressBar())); + } + + // Set the current progress value + m_pProgressBar->setProgress(nProgress, nTotal); +} + +/** + * detsroys any progress widgets when the process is terminated. + */ +void CscopeProgress::finished() +{ + // Destroy the progress bar + if (m_pProgressBar != NULL) { + delete m_pProgressBar; + m_pProgressBar = NULL; + } + + // Destroy the label + if (m_pLabel != NULL) { + delete m_pLabel; + m_pLabel = NULL; + } +} + +/** + * Shows the progress bar. + * This slot is connected to a timer activated when the first progress signal + * is received. + */ +void CscopeProgress::slotShowProgressBar() +{ + if (m_pProgressBar != NULL) + m_pProgressBar->show(); +} + +/** + * Shows the "Please wait...". + * This slot is connected to a timer activated when the progress bar + * reaches its final value. + */ +void CscopeProgress::slotShowLabel() +{ + if (m_pLabel != NULL) + m_pLabel->show(); +} + +void CscopeVerifier::verify() +{ + ConfigFrontend* pConf; + + pConf = new ConfigFrontend(true); + connect(pConf, SIGNAL(result(uint, const QString&)), this, + SLOT(slotConfigResult(uint, const QString&))); + connect(pConf, SIGNAL(finished(uint)), this, SLOT(slotFinished())); + + pConf->run(Config().getCscopePath(), "", "", true); +} + +void CscopeVerifier::slotConfigResult(uint nType, const QString& sResult) +{ + switch (nType) { + case ConfigFrontend::CscopeVerbose: + if (sResult == "Yes") + m_nArgs |= CscopeFrontend::VerboseOut; + break; + + case ConfigFrontend::CscopeSlowPath: + if (sResult == "Yes") + m_nArgs |= CscopeFrontend::SlowPathDef; + + // If we got this far, then Cscope is configured properly + m_bResult = true; + break; + } +} + +void CscopeVerifier::slotFinished() +{ + emit done(m_bResult, m_nArgs); + delete this; +} + +#include "cscopefrontend.moc" diff --git a/src/cscopefrontend.h b/src/cscopefrontend.h new file mode 100644 index 0000000..2b2c569 --- /dev/null +++ b/src/cscopefrontend.h @@ -0,0 +1,186 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef CSCOPEFRONTEND_H +#define CSCOPEFRONTEND_H + +#include <qstringlist.h> +#include <qprogressbar.h> +#include <qlabel.h> +#include "frontend.h" + +#define CSCOPE_RECORD_SIZE 4 + +/** + * Controls a Cscope process for the current project. + * This class creates a Cscope process, using the project's files for + * configuration. Once the process is running, KScope uses the query() method + * to initiate Cscope queries on the project's files. The queries' output is + * parsed into a set of records, each consisting of the following fields: + * - File name + * - Function name + * - Line number + * - The line's text + * These records are used to display the output in different windows, such as + * QueryWidget and CallTreeDlg. + * @author Elad Lahav + */ + +class CscopeFrontend : public Frontend +{ + Q_OBJECT + +public: + CscopeFrontend(bool bAutoDelete = false); + ~CscopeFrontend(); + + /** + * The available Cscope query types. + */ + enum QueryType { Reference = 0, Definition = 1, Called = 2, Calling = 3, + Text = 4, Pattern = 6, FileName = 7, Including = 8, None = 9 }; + + /** + * Options for running Cscope, used to construct the command line. + * Some of these options are global, while some are project specific. + */ + enum Options { VerboseOut = 0x01, SlowPathDef = 0x02, + Kernel = 0x04, InvIndex = 0x08, NoCompression = 0x10 }; + + void query(uint, const QString&, bool bCase = true, uint nMaxRecords = 0); + void rebuild(); + + static void init(const QString&, uint); + + /** + * @param nArgs The command-line arguments supported by the version of + * Cscope currently in use + */ + static void setSupArgs(uint nArgs) { s_nSupArgs = nArgs; } + +public slots: + void slotCancel(); + +signals: + /** + * Emitted when Cscope starts building the inverted index. + */ + void buildInvIndex(); + +protected: + virtual ParseResult parseStdout(QString&, ParserDelim); + virtual void parseStderr(const QString&); + virtual void finalize(); + +private: + /** + * The possible states of the parser state machine. + */ + enum ParserState { Unknown = 0, BuildStart, BuildSymbol, Building, + SearchSymbol, Searching, SearchEnd, File, Func, Line, LineText }; + + /** The current state of the parser state machine. */ + ParserState m_state; + + /** Accumulates text sent by Cscope to the standard error stream. */ + QString m_sErrMsg; + + /** If true, the rebuild process will be restarted when the process + exits. */ + bool m_bRebuildOnExit; + + /** The maximal number of records requested for the current query. + The process aborts if this number if reached. */ + int m_nMaxRecords; + + /** The full path of the directory holding the project files. */ + static QString s_sProjPath; + + /** Project-specific options for the command-line arguments. */ + static uint s_nProjArgs; + + /** The command line arguments supported by this version of Cscope. */ + static uint s_nSupArgs; + + bool run(const QStringList&); +}; + +/** + * Provides progress information on a Cscope query. + * Classes used to display query results can use this class to show a + * progress bar while a query is running, and a "Please Wait..." label while + * output is being processed. + * @author Elad Lahav + */ +class CscopeProgress : public QObject +{ + Q_OBJECT + +public: + CscopeProgress(QWidget*); + ~CscopeProgress(); + + void setProgress(int, int); + void finished(); + +private: + /** The parent widget for the progress bar and label. */ + QWidget* m_pMainWidget; + + /** A bar used to display query progress information. */ + QProgressBar* m_pProgressBar; + + /** A label used to display a "Please wait..." message. */ + QLabel* m_pLabel; + +private slots: + void slotShowProgressBar(); + void slotShowLabel(); +}; + +class CscopeVerifier : public QObject +{ + Q_OBJECT + +public: + CscopeVerifier() : m_bResult(false), m_nArgs(0) {} + + void verify(); + +signals: + void done(bool, uint); + +private: + bool m_bResult; + uint m_nArgs; + +private slots: + void slotConfigResult(uint, const QString&); + void slotFinished(); +}; + +#endif diff --git a/src/cscopemsgdlg.cpp b/src/cscopemsgdlg.cpp new file mode 100644 index 0000000..1bf6656 --- /dev/null +++ b/src/cscopemsgdlg.cpp @@ -0,0 +1,66 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qtextedit.h> +#include <qpushbutton.h> +#include "cscopemsgdlg.h" + +/** + * Class constructor. + * @param pParent The parent widget + * @param szName The widget's name + */ +CscopeMsgDlg::CscopeMsgDlg(QWidget* pParent, const char* szName) + : CscopeMsgLayout(pParent, szName, false, 0) +{ + // Hide the dialog when the "Hide" button is clicked + connect(m_pHideButton, SIGNAL(clicked()), this, SLOT(hide())); + + // Clear all messages when the "Clear" button is clicked + connect(m_pClearButton, SIGNAL(clicked()), m_pMsgText, SLOT(clear())); +} + +/** + * Class destructor. + */ +CscopeMsgDlg::~CscopeMsgDlg() +{ +} + +/** + * Appends a given message to the text box. + * After a new messsage is added, the dialog becomes visible. + * @param sText The text of the message to add + */ +void CscopeMsgDlg::addText(const QString& sText) +{ + m_pMsgText->append(sText); + show(); +} + +#include "cscopemsgdlg.moc" + diff --git a/src/cscopemsgdlg.h b/src/cscopemsgdlg.h new file mode 100644 index 0000000..0cd45cd --- /dev/null +++ b/src/cscopemsgdlg.h @@ -0,0 +1,49 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef CSCOPEMSGDLG_H +#define CSCOPEMSGDLG_H + +#include "cscopemsglayout.h" + +/** + * Displays messages sent by Cscope to its standard error stream. + * @author Elad Lahav + */ +class CscopeMsgDlg : public CscopeMsgLayout +{ + Q_OBJECT + +public: + CscopeMsgDlg(QWidget* pParent = 0, const char* szName = 0); + ~CscopeMsgDlg(); + + void addText(const QString&); +}; + +#endif + diff --git a/src/cscopemsglayout.ui b/src/cscopemsglayout.ui new file mode 100644 index 0000000..1a6458d --- /dev/null +++ b/src/cscopemsglayout.ui @@ -0,0 +1,79 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>CscopeMsgLayout</class> +<widget class="QDialog"> + <property name="name"> + <cstring>CscopeMsgLayout</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>600</width> + <height>451</height> + </rect> + </property> + <property name="caption"> + <string>Cscope Error Messages</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QTextEdit"> + <property name="name"> + <cstring>m_pMsgText</cstring> + </property> + <property name="textFormat"> + <enum>PlainText</enum> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout1</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>321</width> + <height>31</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pClearButton</cstring> + </property> + <property name="text"> + <string>Clear</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pHideButton</cstring> + </property> + <property name="text"> + <string>Hide</string> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/src/ctagsfrontend.cpp b/src/ctagsfrontend.cpp new file mode 100644 index 0000000..73f7519 --- /dev/null +++ b/src/ctagsfrontend.cpp @@ -0,0 +1,179 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qfileinfo.h> +#include <kmessagebox.h> +#include <klocale.h> +#include <kshell.h> +#include "ctagsfrontend.h" +#include "kscopeconfig.h" + +QStringList CtagsFrontend::s_slExtraArgs; + +/** + * Class constructor. + */ +CtagsFrontend::CtagsFrontend() : Frontend(CTAGS_RECORD_SIZE) +{ +} + +/** + * Class destructor. + */ +CtagsFrontend::~CtagsFrontend() +{ +} + +/** + * Executes a Ctags process on a source file. + * @param sFileName The full path to the source file + * @return true if successful, false otherwise + */ +bool CtagsFrontend::run(const QString& sFileName) +{ + QString sPath; + QStringList slArgs; + + // Make sure the executable exists + sPath = Config().getCtagsPath(); + + // Set the command line arguments + slArgs.append(sPath); + slArgs.append("--excmd=n"); + slArgs.append("-u"); // don't sort + slArgs.append("-f"); + slArgs.append("-"); + + // Per-project command-line arguments + slArgs += s_slExtraArgs; + + slArgs.append(sFileName); + + // Run a new process + if (!Frontend::run("ctags", slArgs)) + return false; + + // Initialize stdout parsing + m_state = Name; + m_delim = Tab; + + return true; +} + +/** + * Tests that the given file path leads to an executable. + * @param sPath The path to check + * @return true if the file in the given path exists and has executable + * permissions, false otherwise + */ +bool CtagsFrontend::verify(const QString& sPath) +{ + QFileInfo fi(sPath); + + if (!fi.exists() || !fi.isFile() || !fi.isExecutable() || + fi.fileName().find("ctags", 0, false) == -1) { + KMessageBox::error(0, i18n("Ctags cannot be found in the given " + "path")); + return false; + } + + return true; +} + +/** + * Turns the per-project string of additional arguments into a list of + * command-line arguments. + * @param sArgs The per-project command string + */ +void CtagsFrontend::setExtraArgs(const QString& sArgs) +{ + s_slExtraArgs = KShell::splitArgs(sArgs); +} + +/** + * Parses the output of a Ctags process. + * @param sToken The current token read (the token delimiter is determined + * by the current state) + * @param delim The delimiter that ends this token + * @return A value indicating the way this token should be treated: dropped, + * added to the token queue, or finishes a new record + */ +Frontend::ParseResult CtagsFrontend::parseStdout(QString& sToken, + ParserDelim delim) +{ + ParseResult result = DiscardToken; + + // Handle the token according to the current state + switch (m_state) { + case Name: + if (sToken.left(6) == "ctags:") { + m_state = Other; + m_delim = Newline; + break; + } + + m_state = File; + result = AcceptToken; + break; + + case File: + m_state = Line; + result = DiscardToken; + break; + + case Line: + sToken = sToken.left(sToken.length() - 2); + m_state = Type; + m_delim = All; + result = AcceptToken; + break; + + case Type: + if (delim == Newline) { + m_state = Name; + m_delim = Tab; + } + else { + m_state = Other; + m_delim = Newline; + } + + result = RecordReady; + break; + + case Other: + m_state = Name; + m_delim = Tab; + result = DiscardToken; + break; + + } + + return result; +} + +#include "ctagsfrontend.moc" diff --git a/src/ctagsfrontend.h b/src/ctagsfrontend.h new file mode 100644 index 0000000..3c0c452 --- /dev/null +++ b/src/ctagsfrontend.h @@ -0,0 +1,77 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef CTAGSFRONTEND_H +#define CTAGSFRONTEND_H + +#include <frontend.h> + +#define CTAGS_RECORD_SIZE 3 + +/** + * Controls a Ctags process for an file in an EditorPage window. + * A new Ctags process is run each time the file in the editor window is + * loaded (including the initial load, and any subsequent ones which follow a + * 'save' operation.) + * The output of the process is parsed into a set of records, each composed of + * the following fields: + * - Tag type + * - Tag name + * - Line number + * The records are then displayed in the CtagsList widget that is attached to + * each EditorPage window. + * @author Elad Lahav + */ + +class CtagsFrontend : public Frontend +{ + Q_OBJECT + +public: + CtagsFrontend(); + ~CtagsFrontend(); + + bool run(const QString&); + + static bool verify(const QString&); + static void setExtraArgs(const QString&); + +protected: + virtual ParseResult parseStdout(QString&, ParserDelim); + +private: + /** State values for the parser state machine. */ + enum ParserState { Name = 0, File, Line, Type, Other }; + + /** The current state of the parser state machine. */ + ParserState m_state; + + /** Additional ommand-line arguments (per-project). */ + static QStringList s_slExtraArgs; +}; + +#endif diff --git a/src/ctagslist.cpp b/src/ctagslist.cpp new file mode 100644 index 0000000..687e9fb --- /dev/null +++ b/src/ctagslist.cpp @@ -0,0 +1,446 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qheader.h> +#include <klocale.h> +#include "ctagslist.h" +#include "kscopeconfig.h" +#include "kscopepixmaps.h" + +/** + * Defines a special list item for the tag list. + * This special definition allows the tag list to be sorted according to + * symbol line numbers. By default, all items are treated as text, hence the + * comparison of line numbers such as "123" and "24" sets "24" to be the + * larger item. By overriding the comparison function, this class allows for + * correct sorting. + * @author Elad Lahav + */ +class CtagsListItem : public QListViewItem +{ +public: + /** + * Class constructor. + * @param pParent The owning list view widget + * @param sName The name of the tag + * @param sLine The line in which the tag is defined + * @param sType The type of the tag + */ + CtagsListItem(QListView* pParent, QString sName, QString sLine, + QString sType) : QListViewItem(pParent, sName, sLine, sType), + m_nPendLine (sLine.toUInt()) {} + + /** + * Compares two tag list items, and determines their order. + * If comparison is based on a text-column, the default behaviour is + * used. Otherwise, the text is converted to unsigned integers, and then + * compared as numbers. + * @param pItem The item to compare against the local object + * @param nCol The column index by which to compare + * @param bAscend true if sorting in ascending order, false otherwise + * @return 0 if the items are equal, 1 if the local item is greater, -1 + * if the local item is lesser + */ + virtual int compare(QListViewItem* pItem, int nCol, bool bAscend) const { + if (nCol == 1) { + uint nLineCur, nLineOther; + int nResult; + + // Get the line numbers of each item + nLineCur = text(1).toUInt(); + nLineOther = pItem->text(1).toUInt(); + + // Compare the line numbers + nResult = nLineCur - nLineOther; + if (nResult == 0) + return 0; // Items are equal + else if (nResult > 0) + return 1; // The first item is greater + else + return -1; // The second item is greater + } + + // Use default comparison for text columns + return QListViewItem::compare(pItem, nCol, bAscend); + } + + /** + * @return The line number associated with this item + */ + inline uint getLine() { return m_nPendLine; } + +private: + /** The numeric value of the line number column of this item. */ + uint m_nPendLine; +}; + +/** + * Class constructor. + * @param pParent The parent widget + * @param szName The widget's name + */ +CtagsList::CtagsList(QWidget* pParent, const char* szName) : + SearchList(0, pParent, szName), + m_arrLines(16), + m_nItems(0), + m_nCurItem(0), + m_bReady(false), + m_nCurLine(0), + m_nPendLine(0) +{ + m_pList->setShowSortIndicator(true); + connect(m_pList->header(), SIGNAL(clicked(int)), this, + SLOT(slotSortChanged(int))); + + // Determine the default sorting order + switch (Config().getCtagSortOrder()) { + case KScopeConfig::NameAsc: + m_pList->setSorting(0, true); + break; + + case KScopeConfig::NameDes: + m_pList->setSorting(0, false); + break; + + case KScopeConfig::LineAsc: + m_pList->setSorting(1, true); + break; + + case KScopeConfig::LineDes: + m_pList->setSorting(1, false); + break; + + case KScopeConfig::TypeAsc: + m_pList->setSorting(2, true); + break; + + case KScopeConfig::TypeDes: + m_pList->setSorting(2, false); + break; + } + + // Add the list columns + m_pList->addColumn(i18n("Name")); + m_pList->addColumn(i18n("Line")); + m_pList->addColumn(i18n("Type")); + m_pList->setColumnAlignment(1, Qt::AlignRight); + + // Set colours and font + applyPrefs(); +} + +/** + * Class destructor. + */ +CtagsList::~CtagsList() +{ +} + +/** + * Adds a Ctags output entry to the list. + * This slot is connected to the dataReady() signal of a CtagsFrontend object. + * @param pToken The first token in the entry + */ +void CtagsList::slotDataReady(FrontendToken* pToken) +{ + QString sName, sType, sLine; + CtagsListItem* pItem; + KScopePixmaps::PixName pix; + + // Get the name of the symbol + sName = pToken->getData(); + pToken = pToken->getNext(); + + // Get the line number + sLine = pToken->getData(); + pToken = pToken->getNext(); + + // Get the type of the symbol + sType = pToken->getData(); + pToken = pToken->getNext(); + + // Set the appropriate pixmap + switch (sType[0].latin1()) { + case 'f': + sType = i18n("Function"); + pix = KScopePixmaps::SymFunc; + break; + + case 'v': + sType = i18n("Variable"); + pix = KScopePixmaps::SymVar; + break; + + case 's': + sType = i18n("Struct"); + pix = KScopePixmaps::SymStruct; + break; + + case 'd': + sType = i18n("Macro"); + pix = KScopePixmaps::SymMacro; + break; + + case 'm': + sType = i18n("Member"); + pix = KScopePixmaps::SymMember; + break; + + case 'g': + sType = i18n("Enum"); + pix = KScopePixmaps::SymEnum; + break; + + case 'e': + sType = i18n("Enumerator"); + pix = KScopePixmaps::SymEnumerator; + break; + + case 't': + sType = i18n("Typedef"); + pix = KScopePixmaps::SymTypedef; + break; + + case 'l': + sType = i18n("Label"); + pix = KScopePixmaps::SymLabel; + break; + + case 'i': + sType = i18n("Include"); + pix = KScopePixmaps::SymInclude; + break; + + default: + sType = "Unknown"; + pix = KScopePixmaps::SymUnknown; + } + + // Add a new item to the list + pItem = new CtagsListItem(m_pList, sName, sLine, sType); + pItem->setPixmap(0, Pixmaps().getPixmap(pix)); + m_nItems++; + + // Resize the line array, if required + if (m_arrLines.size() < m_nItems) + m_arrLines.resize(m_nItems, QGArray::SpeedOptim); + + // Add the new item to the line array + m_arrLines[m_nItems - 1] = pItem; +} + +/** + * Handles the "resize" event, which occurs when the size of the widget + * changes. + * @param pEvent The event data + */ +void CtagsList::resizeEvent(QResizeEvent* pEvent) +{ + SearchList::resizeEvent(pEvent); + emit resized(); +} + +/** + * Emits the lineRequested() signal when a list item is selected. + * This function is called if either an item is double-clicked, or an item is + * highlighted and the ENTER key is pressed. + * @param pItem The selected list item + */ +void CtagsList::processItemSelected(QListViewItem* pItem) +{ + QString sLine; + + sLine = pItem->text(1); + emit lineRequested(sLine.toUInt()); +} + +/** + * Constructs a tool-tip for the given item. + * @param pItem The item for which a tip is required + * @param sTip The constructed tip string (on return) + * @return Always true + */ +bool CtagsList::getTip(QListViewItem* pItem, QString& sTip) +{ + sTip = QString("Type: <b>%1</b><br>Name: <b>%2</b><br>Line: <b>%3</b>"). + arg(pItem->text(2)).arg(pItem->text(0)).arg(pItem->text(1)); + return true; +} + +/** + * Sets the list's colours and font, according the user's preferences. + */ +void CtagsList::applyPrefs() +{ + // Apply colour settings + m_pList->setPaletteBackgroundColor(Config().getColor( + KScopeConfig::TagListBack)); + m_pList->setPaletteForegroundColor(Config().getColor( + KScopeConfig::TagListFore)); + m_pList->setFont(Config().getFont(KScopeConfig::TagList)); +} + +/** + * Selects the symbol that dominates the given line in the source file. + * @param nLine The requested line + */ +void CtagsList::gotoLine(uint nLine) +{ + CtagsListItem* pItem; + int nFrom, nTo, nItem, nDiff; + + // Wait until Ctags finishes + if (!m_bReady) { + m_nPendLine = nLine; + return; + } + + // Do nothing if no tags are available + if (m_nItems == 0) + return; + + // Calculate the difference from the current line + nDiff = (int)(nLine - m_nCurLine); + m_nCurLine = nLine; + + // In most cases, all the user does is move to the next or prevuious lines + // Handle these simple cases first + if (nDiff == 1) { + if ((m_nCurItem < m_nItems - 1) && + (m_arrLines[m_nCurItem + 1]->getLine() == nLine)) { + m_nCurItem++; + } + else { + return; // New line corresponds to the same tag + } + } + else if (nDiff == -1) { + if ((m_nCurItem > 0) && + (m_arrLines[m_nCurItem]->getLine() > nLine)) { + m_nCurItem--; + } + else { + return; // New line corresponds to the same tag + } + } + else { + // Initialise binary search + nFrom = 0; + nTo = m_nItems - 1; + m_nCurItem = 0; // use the first item if nothing else works + + // Perform a binary search + // This algorithm finds the greatest line that is smaller or equal to + // the requested line + do { + nItem = (nFrom + nTo) / 2; + pItem = m_arrLines[nItem]; + + if (pItem->getLine() == nLine) { + m_nCurItem = nItem; + break; + } + else if (nLine > pItem->getLine()) { + m_nCurItem = nItem; + nFrom = nItem + 1; + } + else { + nTo = nItem - 1; + } + } while (nFrom <= nTo); + } + + // Mark the selected item + pItem = m_arrLines[m_nCurItem]; + m_pList->setSelected(pItem, true); + m_pList->ensureItemVisible(pItem); + + m_nPendLine = 0; +} + +/** + * Deletes all items in the list. + */ +void CtagsList::clear() +{ + m_pList->clear(); + m_nItems = 0; + m_nCurItem = 0; + m_nCurLine = 0; + m_nPendLine = 0; + m_bReady = false; +} + +/** + * Indicates Ctags has finished processing the current file. + * If a goto operation has been scheduled, it is processed. + * @param nRecords The number of records generated by Ctags + */ +void CtagsList::slotCtagsFinished(uint nRecords) +{ + if (nRecords) { + m_bReady = true; + if (m_nPendLine) + gotoLine(m_nPendLine); + } +} + +/** + * Determines the new sort order in the tags list. + * This slot is connected to the clicked() signal of the tag list's header. + * @param nSection Identifies the column whose header button was clicked. + */ +void CtagsList::slotSortChanged(int nSection) +{ + Qt::SortOrder order; + + // Determine whether the new order is ascending or descending + order = m_pList->sortOrder(); + + // Translate the section number into the order constant + switch (nSection) { + case 0: + // Sort by name + Config().setCtagSortOrder(order == Qt::Ascending ? + KScopeConfig::NameAsc : KScopeConfig::NameDes); + break; + + case 1: + // Sort by line + Config().setCtagSortOrder(order == Qt::Ascending ? + KScopeConfig::LineAsc : KScopeConfig::LineDes); + break; + + case 2: + // Sort by type + Config().setCtagSortOrder(order == Qt::Ascending ? + KScopeConfig::TypeAsc : KScopeConfig::TypeDes); + break; + } +} + +#include "ctagslist.moc" diff --git a/src/ctagslist.h b/src/ctagslist.h new file mode 100644 index 0000000..4d318cc --- /dev/null +++ b/src/ctagslist.h @@ -0,0 +1,108 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef CTAGSLIST_H +#define CTAGSLIST_H + +#include <qwidget.h> +#include <qpixmap.h> +#include <qmemarray.h> +#include "searchlist.h" +#include "frontend.h" + +class CtagsListItem; +class CtagsToolTip; + +/** + * Displays a list of tags for a source file. + * The list is embedded inside an editor page. Whenever a new document is + * opened in that editor, or the current document is changed and saved, the + * source file is re-scanned for tags, and the results are displayed in this + * list. + * @author Elad Lahav + */ + +class CtagsList : public SearchList +{ + Q_OBJECT + +public: + CtagsList(QWidget* pParent = 0, const char* szName = 0); + ~CtagsList(); + + void applyPrefs(); + void gotoLine(uint); + void clear(); + + virtual bool getTip(QListViewItem*, QString&); + +public slots: + void slotDataReady(FrontendToken*); + void slotCtagsFinished(uint); + +signals: + /** + * Emitted when the size of the list is changed (usually as the result + * of moving the separator between the list and the editor.) + */ + void resized(); + + /** + * Emitted when the user selects a tag item from the list. + * @param nLine The line number associated with the selected tag + */ + void lineRequested(uint nLine); + +protected: + virtual void resizeEvent(QResizeEvent*); + virtual void processItemSelected(QListViewItem*); + +private: + /** An array of pointers to the tag list items, sorted by the line + number. */ + QMemArray<CtagsListItem*> m_arrLines; + + /** The number of items in the tag list. */ + uint m_nItems; + + /** The last item selected by gotoLine(). */ + uint m_nCurItem; + + /** This value is set to 'false' while the Ctags process is running. */ + bool m_bReady; + + /** The current line number. */ + uint m_nCurLine; + + /** Stores the requested line number during Ctags operation. */ + uint m_nPendLine; + +private slots: + void slotSortChanged(int); +}; + +#endif diff --git a/src/dirscanner.cpp b/src/dirscanner.cpp new file mode 100644 index 0000000..517237e --- /dev/null +++ b/src/dirscanner.cpp @@ -0,0 +1,164 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qapplication.h> +#include "dirscanner.h" + +/** + * Class constructor. + * @param nFiles The number of files scanned since the previous event + * @param bFinished true if all files were scanned, false otherwise + */ +DirScanEvent::DirScanEvent(int nFiles, bool bFinished) + : QCustomEvent(EventId), + m_nFiles(nFiles), + m_bFinished(bFinished) +{ +} + +/** + * Class constructor. + * @param pEventReceiver Pointer to an object to receive DirScanEvent + * updates + * @param pDicFiles Pointer to a map of current project files (to + avoid duplication) + */ +DirScanner::DirScanner(QObject* pEventReceiver, + QDict<QListViewItem>* pDicFiles) : QThread(), + m_pEventReceiver(pEventReceiver), + m_pDicFiles(pDicFiles) +{ +} + +/** + * Class destructor. + */ +DirScanner::~DirScanner() +{ +} + +/** + * Begins a new search for source files. + * Invokes the search thread on a given directory. The search may be either + * recursive (i.e., the search will descend to each sub-directory) or flat + * (will search the given directory only.) + * @param sDir The name of the directory to search + * @param sNameFilter Defines the search pattern + * @param bRecursive true to descend into sub-dorectories, false otherwise + */ +void DirScanner::start(const QString& sDir, const QString& sNameFilter, + bool bRecursive) +{ + // Initialise the search parameters + m_dir = QDir(sDir); + m_sNameFilter = sNameFilter; + m_bCancel = false; + m_bRecursive = bRecursive; + m_slFiles.clear(); + + // Invoke the thread + QThread::start(); +} + +/** + * Begins a scan of files on the directory associated with this object. + * Note that this function is synchronous: it returns when the scan ends. + */ +void DirScanner::run() +{ + int nFiles; + + nFiles = scanDir(m_dir); + QApplication::postEvent(m_pEventReceiver, + new DirScanEvent(nFiles, true)); + + m_setScanned.clear(); +} + +/** + * Recursively scans a directory for a files matching the current filter. + * @param dir A directory object set to the folder from which files are + * added + * @return The total number of files added + */ +int DirScanner::scanDir(QDir& dir) +{ + QString sCanon; + QStringList slDirFiles, slDirs; + QStringList::const_iterator itr; + QString sFile; + int nFiles = 0; + + if (m_bCancel) + return -1; + + // Make sure this directory has not been previously visited (e.g., through a + // symbolic link) + sCanon = dir.canonicalPath(); + if (m_setScanned.exists(sCanon)) + return 0; + + m_setScanned.insert(sCanon); + + // Add all files in this directory + slDirFiles = dir.entryList(m_sNameFilter, QDir::Files); + for (itr = slDirFiles.begin(); itr != slDirFiles.end(); ++itr) { + sFile = dir.absPath() + "/" + *itr; + + // Make sure an entry for this file does not exist + if (m_pDicFiles->find(sFile) == NULL) { + m_slFiles.append(sFile); + nFiles++; + } + } + + QApplication::postEvent(m_pEventReceiver, + new DirScanEvent(nFiles, false)); + + // Recurse into sub-directories, if requested + if (!m_bRecursive) + return nFiles; + + slDirs = dir.entryList(QDir::Dirs); + + // Iterate the list of sub-directories + for (itr = slDirs.begin(); itr != slDirs.end(); ++itr) { + if (m_bCancel) + return -1; + + // Skip the "." and ".." directories + if (*itr == "." || *itr == "..") + continue; + + // Add the files in each sub-directory + QDir dirSub(dir); + if (dirSub.cd(*itr)) + nFiles += scanDir(dirSub); + } + + return nFiles; +} diff --git a/src/dirscanner.h b/src/dirscanner.h new file mode 100644 index 0000000..b25fc2d --- /dev/null +++ b/src/dirscanner.h @@ -0,0 +1,144 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef DIRSCANNER_H +#define DIRSCANNER_H + +#include <qobject.h> +#include <qevent.h> +#include <qthread.h> +#include <qdir.h> +#include <qstringlist.h> +#include <qdict.h> +#include <qlistview.h> + +class DirScanner; + +/** + * Defines a new event that can be used to pass progress information from the + * dir scanning thread to the main application thread. + * @author Elad Lahav + */ +class DirScanEvent : public QCustomEvent +{ +public: + /** The event's unique ID. */ + enum { EventId = 6924 }; + + DirScanEvent(int, bool); + + /** The number of files already scanned. */ + int m_nFiles; + + /** True if the dir scanning thread has finished, false otherwise. */ + bool m_bFinished; +}; + +/** + * A set of unique strings. + * Qt3 does not have a set class, so this is a simple implementation based on + * a QDict of dummy int pointers. + * @author Elad Lahav + */ +class StringSet : public QDict<int> +{ +public: + StringSet() : QDict<int>() {} + + void insert(const QString& sItem) { + static int nDummy = 0; + QDict<int>::insert(sItem, &nDummy); + } + + bool exists(const QString& sItem) { + return find(sItem) != NULL; + } +}; + +/** + * Scans a directory for files matching a given pattern, using a separate thread. + * @author Elad Lahav + */ +class DirScanner : public QThread +{ +public: + DirScanner(QObject*, QDict<QListViewItem>*); + ~DirScanner(); + + void start(const QString&, const QString&, bool); + + /** + * @return The list of files scanned by this thread. + */ + const QStringList& getFiles() { return m_slFiles; } + + /** + * Stops a scanning process, by setting the object's cancel flag. + */ + void cancel() { m_bCancel = true; } + + /** + * @return true if the user has cancelled the process, false otherwise + */ + bool wasCancelled() { return m_bCancel; } + +protected: + virtual void run(); + +private: + /** Pointer to an object that receives the scanner update events. */ + QObject* m_pEventReceiver; + + /** Currently scanned directory. */ + QDir m_dir; + + /** + * A set of already-scanned directories (prevents infinite loops in case + * of cyclic symbolic links in the scanned file system). + */ + StringSet m_setScanned; + + /** Pointer to a list of files indexed by the file path (used to identify + files that should not appear in the scan results.) */ + QDict<QListViewItem>* m_pDicFiles; + + /** Regular expression for scanning source files. */ + QString m_sNameFilter; + + /** The list of scanned file paths. */ + QStringList m_slFiles; + + /** A cancellation flag. Stops the scanning process when raised. */ + bool m_bCancel; + + /** true to descend to child directories, false otherwise. */ + bool m_bRecursive; + + int scanDir(QDir&); +}; + +#endif diff --git a/src/dotfrontend.cpp b/src/dotfrontend.cpp new file mode 100644 index 0000000..e0cfbed --- /dev/null +++ b/src/dotfrontend.cpp @@ -0,0 +1,297 @@ +/*************************************************************************** + * + * Copyright (C) 2006 Elad Lahav (elad_lahav@users.sf.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qfileinfo.h> +#include <qpaintdevicemetrics.h> +#include <kmessagebox.h> +#include <klocale.h> +#include "dotfrontend.h" +#include "graphwidget.h" +#include "kscopeconfig.h" + +/** + * Class constructor. + * @param pGraph The graph widget on which to draw the output + */ +DotFrontend::DotFrontend(GraphWidget* pGraph) : Frontend(1), + m_pGraph(pGraph) +{ +} + +/** + * Class destructor. + */ +DotFrontend::~DotFrontend() +{ +} + +/** + * Executes dot on the goven input file. + * @param sFile The path to a temporary file holding the graph's + * description + * @return true if successful, false otherwise + */ +bool DotFrontend::run(const QString& sFile) +{ + QString sPath; + QStringList slArgs; + QPaintDeviceMetrics pdm(m_pGraph); + + // Set the horizontal and vertical DPI values + m_dDpiX = (double)pdm.logicalDpiX(); + m_dDpiY = (double)pdm.logicalDpiY(); + + // Make sure the executable exists + sPath = Config().getDotPath(); + + // Set the command line arguments + slArgs.append(sPath); + slArgs.append("-Tplain"); + slArgs.append(sFile); + + // Run a new process + if (!Frontend::run("dot", slArgs)) + return false; + + // Initialize stdout parsing + m_state = Graph; + m_delim = All; + + return true; +} + +/** + * Tests that the given file path leads to an executable. + * @param sPath The path to check + * @return true if the file in the given path exists and has executable + * permissions, false otherwise + */ +bool DotFrontend::verify(const QString& sPath) +{ + QFileInfo fi(sPath); + + if (!fi.exists() || !fi.isFile() || !fi.isExecutable() || + fi.fileName() != "dot") { + KMessageBox::error(0, i18n("Dot cannot be found in the given " + "path")); + return false; + } + + return true; +} + +#define PAD 5 + +/** + * Parses the output of a Dot process. + * @param sToken The current token read (the token delimiter is determined + * by the current state) + * @param delim The delimiter that ends this token + * @return A value indicating the way this token should be treated: dropped, + * added to the token queue, or finishes a new record + */ +Frontend::ParseResult DotFrontend::parseStdout(QString& sToken, + ParserDelim delim) +{ + static int nWidth, nHeight, nXpos, nYpos, nCurveSize, nCurveCount; + static QPointArray arrCurve; + static QString sNode, sEdgeHead, sEdgeTail; + ParseResult result = DiscardToken; + double dVal; + bool bOK; + + // Handle the token according to the current state + switch (m_state) { + case Graph: + if (sToken == "graph") + m_state = GraphScale; + break; + + case GraphScale: + sToken.toDouble(&bOK); + if (bOK) + m_state = GraphWidth; + break; + + case GraphWidth: + // Read and transform the graph's width + dVal = sToken.toDouble(&bOK); + if (bOK) { + nWidth = (int)(dVal * m_dDpiX) + (PAD * 2); + m_state = GraphHeight; + } + break; + + case GraphHeight: + // Read and transform the graph's height + dVal = sToken.toDouble(&bOK); + if (bOK) { + nHeight = (int)(dVal * m_dDpiY) + (PAD * 2); + + // Set the graph's size + m_pGraph->resize(nWidth, nHeight); + + m_state = NodeEdgeStop; + } + break; + + case NodeEdgeStop: + // "node" starts a new node + // "edge" starts a new edge + // "stop" ends this graph + if (sToken == "node") { + m_state = NodeName; + } + else if (sToken == "edge") { + m_state = EdgeHead; + } + else if (sToken == "stop") { + m_state = Graph; + } + break; + + case NodeName: + // Get a node's name + sNode = sToken; + m_state = NodeCentreX; + break; + + case NodeCentreX: + // Read and transform the node's centre location (X coordinate) + dVal = sToken.toDouble(&bOK); + if (bOK) { + nXpos = (int)(dVal * m_dDpiX) + PAD; + m_state = NodeCentreY; + } + break; + + case NodeCentreY: + // Read and transform the node's centre location (Y coordinate) + dVal = sToken.toDouble(&bOK); + if (bOK) { + nYpos = (int)(dVal * m_dDpiY) + PAD; + m_state = NodeWidth; + } + break; + + case NodeWidth: + // Read and transform the node's width + dVal = sToken.toDouble(&bOK); + if (bOK) { + nWidth = (int)(dVal * m_dDpiX); + m_state = NodeHeight; + } + break; + + case NodeHeight: + // Read and transform the node's height + dVal = sToken.toDouble(&bOK); + if (bOK) { + nHeight = (int)(dVal * m_dDpiY); + + // Create the bounding rectangle of the node + QRect rect; + rect.setX(nXpos - (nWidth / 2)); + rect.setY(nYpos - (nHeight / 2)); + rect.setWidth(nWidth); + rect.setHeight(nHeight); + + // Draw the node + m_pGraph->drawNode(sNode, rect); + + m_state = EndNodeEdge; + } + break; + + case EdgeHead: + // Get the edge's head node + sEdgeHead = sToken; + m_state = EdgeTail; + break; + + case EdgeTail: + // Get the edge's tail node + sEdgeTail = sToken; + m_state = EdgeCurveSize; + break; + + case EdgeCurveSize: + // Get the number of control points in the edge's spline + nCurveSize = sToken.toInt(&bOK); + if (bOK) { + arrCurve.resize(nCurveSize); + nCurveCount = 0; + m_state = EdgeCurveX; + } + break; + + case EdgeCurveX: + // Read and a control point (X coordinate) + dVal = sToken.toDouble(&bOK); + if (bOK) { + nXpos = (int)(dVal * m_dDpiX) + PAD; + m_state = EdgeCurveY; + } + break; + + case EdgeCurveY: + // Read and a control point (Y coordinate) + dVal = sToken.toDouble(&bOK); + if (bOK) { + nYpos = (int)(dVal * m_dDpiY) + PAD; + + // Add the control point to the spline array + arrCurve.setPoint(nCurveCount++, nXpos, nYpos); + + // Check if this is the last control point + if (nCurveCount == nCurveSize) { + // Draw the edge + m_pGraph->drawEdge(sEdgeHead, sEdgeTail, arrCurve); + + // Must detach from contents since a QPointArray shares data + arrCurve.detach(); + + m_state = EndNodeEdge; + } + else { + // Another control point available + m_state = EdgeCurveX; + } + } + break; + + case EndNodeEdge: + // Discard everything else on a node or edge line + if (delim == Newline) + m_state = NodeEdgeStop; + break; + } + + return result; +} + +#include "dotfrontend.moc" diff --git a/src/dotfrontend.h b/src/dotfrontend.h new file mode 100644 index 0000000..24f14f0 --- /dev/null +++ b/src/dotfrontend.h @@ -0,0 +1,76 @@ +/*************************************************************************** + * + * Copyright (C) 2006 Elad Lahav (elad_lahav@users.sf.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ +#ifndef DOTFRONTEND_H +#define DOTFRONTEND_H + +#include <frontend.h> +#include <qpointarray.h> + +class GraphWidget; + +/** + * Front-end for executing graphviz's command-line tool. + * This tool accepts the description of a graph in the 'dot' language, and + * outputs a set of drawing instructions for the graph. + * @author Elad Lahav + */ +class DotFrontend : public Frontend +{ + Q_OBJECT + +public: + DotFrontend(GraphWidget*); + ~DotFrontend(); + + bool run(const QString&); + + static bool verify(const QString&); + +protected: + virtual ParseResult parseStdout(QString&, ParserDelim); + +private: + /** The owner graph widget on which to draw. */ + GraphWidget* m_pGraph; + + /** State values for the parser state machine. */ + enum ParserState { Graph, GraphScale, GraphWidth, GraphHeight, + NodeEdgeStop, NodeName, NodeCentreX, NodeCentreY, NodeWidth, NodeHeight, + EdgeHead, EdgeTail, EdgeCurveSize, EdgeCurveX, EdgeCurveY, + EndNodeEdge }; + + /** The current state of the parser state machine. */ + ParserState m_state; + + /** The horizontal DPI value of the graph widget. */ + double m_dDpiX; + + /** The vertical DPI value of the graph widget. */ + double m_dDpiY; +}; + +#endif diff --git a/src/dotparse.ypp b/src/dotparse.ypp new file mode 100644 index 0000000..f21b9e3 --- /dev/null +++ b/src/dotparse.ypp @@ -0,0 +1,234 @@ +/* dot.y */ + +%{ +#include <qdict.h> +#include <qptrstack.h> +#include <qlistview.h> +#include "calltreedlg.h" +#include "graphwidget.h" +#include "treewidget.h" +#include "encoder.h" + +extern FILE* yyin; +int yylex(); +void yyinit(CallTreeDlg*, FILE*, Encoder*); +void yyerror(const char*); + +static QMap<QString, QString> s_pMapAttr; +static QStack<QListViewItem> s_pParentStack; +static QListView* s_pTree; + +static GraphWidget* s_pGraph; +static TreeWidget* s_pCallTree; +static TreeWidget* s_pCallingTree; +static Encoder* s_pEncoder; + +// Avoid compiler warnings +#define YYENABLE_NLS 0 +#ifndef YYLTYPE_IS_TRIVIAL +#define YYLTYPE_IS_TRIVIAL 0 +#endif +%} + +%union { + QString* pText; +} + +%token GRAPH DIGRAPH NODE NAME STRING NUMBER DIR_EDGE UNDIR_EDGE +%token CALL_TREE CALLING_TREE +%type <pText> NAME STRING NUMBER attr_val + +%start file + +%% + +file + : call_tree calling_tree graph + +graph + : graph_type NAME '{' graph_desc_list '}' { delete $2; } + ; + +graph_type + : GRAPH + | DIGRAPH + ; + +graph_desc_list + : + | graph_desc_entry graph_desc_list + ; + +graph_desc_entry + : def_node_attr + | graph_attr + | node_record + | edge_record + ; + +def_node_attr + : NODE attributes ';' + ; + +graph_attr + : GRAPH attributes ';' + { + if (s_pMapAttr.find("kscope_zoom") != s_pMapAttr.end()) + s_pGraph->setZoom(s_pMapAttr["kscope_zoom"].toDouble()); + } + ; + +node_record + : NAME attributes ';' + { + s_pGraph->addNode(*$1); + delete $1; + s_pMapAttr.clear(); + } + ; + +edge_record + : NAME edge_type NAME attributes ';' + { + GraphWidget::CallData cd; + + cd.m_sCaller = *$1; + cd.m_sCallee = *$3; + cd.m_sFile = s_pMapAttr["kscope_file"]; + cd.m_sLine = s_pMapAttr["kscope_line"]; + cd.m_sText = s_pEncoder->decode(s_pMapAttr["kscope_text"]); + s_pGraph->addCall(cd); + + delete $1; + delete $3; + s_pMapAttr.clear(); + } + ; + +edge_type + : DIR_EDGE + | UNDIR_EDGE + ; + +attributes + : + | '[' attr_list ']' + ; + +attr_list + : + | non_empty_attr_list + ; + +non_empty_attr_list + : attr + | attr ',' attr_list + ; + +attr + : NAME '=' attr_val + { + s_pMapAttr.insert(*$1, *$3); + delete $1; + delete $3; + } + ; + +attr_val + : NAME + | STRING + | NUMBER + ; + +call_tree + : call_tree_prepare '{' root_node '}' + ; + +call_tree_prepare + : CALL_TREE { s_pTree = s_pCallTree; } + ; + +calling_tree + : calling_tree_prepare '{' root_node '}' + ; + +calling_tree_prepare + : CALLING_TREE { s_pTree = s_pCallingTree; } + ; + +root_node + : root_tree_node '{' child_list '}' + { + QListViewItem* pItem; + + pItem = s_pParentStack.pop(); + if (pItem->firstChild() != NULL) + pItem->setOpen(true); + } + ; + +root_tree_node + : NAME + { + QListViewItem* pItem; + + pItem = new QListViewItem(s_pTree, *$1); + s_pParentStack.push(pItem); + delete $1; + } + ; + +child_list + : + | child_node child_list + ; + +child_node + : tree_node tree_attributes '{' child_list '}' + { + QListViewItem* pItem; + + pItem = s_pParentStack.pop(); + if (pItem->firstChild() != NULL) + pItem->setOpen(true); + } + ; + +tree_node + : NAME + { + QListViewItem* pItem; + + pItem = new QListViewItem(s_pParentStack.top(), *$1); + s_pParentStack.push(pItem); + delete $1; + } + ; + +tree_attributes + : attributes + { + QListViewItem* pItem; + + pItem = s_pParentStack.top(); + pItem->setText(1, s_pMapAttr["kscope_file"]); + pItem->setText(2, s_pMapAttr["kscope_line"]); + pItem->setText(3, s_pEncoder->decode(s_pMapAttr["kscope_text"])); + } + ; + +%% + +void yyinit(CallTreeDlg* pDlg, FILE* pFile, Encoder* pEnc) +{ + yyin = pFile; + s_pCallTree = pDlg->m_pCalledWidget; + s_pCallingTree = pDlg->m_pCallingWidget; + s_pGraph = pDlg->m_pGraphWidget; + s_pEncoder = pEnc; +} + +void yyerror(const char* szError) +{ + fprintf(stderr, "%s\n", szError); +} diff --git a/src/dotscan.lpp b/src/dotscan.lpp new file mode 100644 index 0000000..0667d33 --- /dev/null +++ b/src/dotscan.lpp @@ -0,0 +1,36 @@ +/* dot.l */ + +%{ +#include <qstring.h> +#include "dotparse.h" +%} + +%option noyywrap + +name [a-zA-Z_][a-zA-Z0-9_]* +string \"(\\.|[^\"])*\" +space [ \t\n]+ +number [1-9][0-9]* +float [0-9]*\.[0-9]+ + +%% + +"graph" return GRAPH; +"digraph" return DIGRAPH; +"calltree" return CALL_TREE; +"callingtree" return CALLING_TREE; +"node" return NODE; +"->" return DIR_EDGE; +"--" return UNDIR_EDGE; +{name} { yylval.pText = new QString(yytext); return NAME; } +{string} { + QString str = &yytext[1]; + yylval.pText = new QString(str.left(yyleng - 2)); + return STRING; + } +{number} { yylval.pText = new QString(yytext); return NUMBER; } +{float} { yylval.pText = new QString(yytext); return NUMBER; } +{space} ; +. return yytext[0]; + +%% diff --git a/src/editormanager.cpp b/src/editormanager.cpp new file mode 100644 index 0000000..253bf6c --- /dev/null +++ b/src/editormanager.cpp @@ -0,0 +1,89 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <ktexteditor/editorchooser.h> +#include <kate/document.h> +#include "editormanager.h" +#include "kscopeconfig.h" + +/** + * Class constructor. + * @param pParent The parent widget + * @param szName The widget's name + */ +EditorManager::EditorManager(QWidget* pParent, const char* szName) : + KParts::PartManager(pParent, szName) +{ + applyPrefs(); +} + +/** + * Class destructor. + */ +EditorManager::~EditorManager() +{ +} + +/** + * Creates a new document part. + * @return A pointer to the new document + */ +KTextEditor::Document* EditorManager::add() +{ + KTextEditor::Document* pDoc; + + // Create the document + pDoc = KTextEditor::EditorChooser::createDocument(this); + addPart(pDoc); + + return pDoc; +} + +/** + * Deletes a document part. + * @param pDoc The document to remove + */ +void EditorManager::remove(KTextEditor::Document* pDoc) +{ + removePart(pDoc); + delete pDoc; +} + +/** + * Applies the user preferences. + * Determines if Kate warnings are displayed in case the currently edited + * file is modified outside KScope. + * NOTE: This behaviour is determined by a static function, which is why this + * code appears here, rather then for every EditorPage object. + */ +void EditorManager::applyPrefs() +{ + Kate::Document::setFileChangedDialogsActivated( + Config().getWarnModifiedOnDisk()); +} + +#include "editormanager.moc" diff --git a/src/editormanager.h b/src/editormanager.h new file mode 100644 index 0000000..413ffed --- /dev/null +++ b/src/editormanager.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef EDITORMANAGER_H +#define EDITORMANAGER_H + +#include <qwidget.h> +#include <kparts/partmanager.h> +#include <klibloader.h> +#include <ktexteditor/document.h> + +/** + * Creates text editor parts, used to open source files. + * The EditorManager is responsible for creating parts, and managing their + * GUI integration. + * @author Elad Lahav + */ + +class EditorManager : public KParts::PartManager +{ + Q_OBJECT + +public: + EditorManager(QWidget* pParent = 0, const char* szName = 0); + ~EditorManager(); + + KTextEditor::Document* add(); + void remove(KTextEditor::Document*); + void applyPrefs(); +}; + +#endif diff --git a/src/editorpage.cpp b/src/editorpage.cpp new file mode 100644 index 0000000..5c183b9 --- /dev/null +++ b/src/editorpage.cpp @@ -0,0 +1,720 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qfileinfo.h> +#include <kdeversion.h> +#include <ktexteditor/selectioninterface.h> +#include <ktexteditor/viewcursorinterface.h> +#include <ktexteditor/popupmenuinterface.h> +#include <ktexteditor/editinterface.h> +#include <kate/document.h> +#include <kate/view.h> +#include "editorpage.h" +#include "kscopeconfig.h" + +/** + * Class constructor. + * @param pDoc The document object associated with this page + * @param pMenu A Cscope queries popup menu to use with the editor + * @param pParent The parent widget + * @param szName The widget's name + */ +EditorPage::EditorPage(KTextEditor::Document* pDoc, QPopupMenu* pMenu, + QTabWidget* pParent, const char* szName) : QHBox(pParent, szName), + m_pParentTab(pParent), + m_pDoc(pDoc), + m_bOpen(false), + m_bNewFile(false), + m_sName(""), + m_bWritable(true), /* new documents are writable by default */ + m_bModified(false), + m_nLine(0), + m_bSaveNewSizes(false) +{ + KTextEditor::PopupMenuInterface* pMenuIf; + KTextEditor::ViewCursorInterface* pCursorIf; + + // Create code-completion objects (will be deleted by QObject destructor) + m_pCompletion = new SymbolCompletion(this, this); + + // Set read-only mode, if required + if (Config().getReadOnlyMode()) + m_pDoc->setReadWrite(false); + + // Create the child widgets + m_pSplit = new QSplitter(this); + m_pCtagsList = new CtagsList(m_pSplit); + m_pView = m_pDoc->createView(m_pSplit); + m_pSplit->setResizeMode(m_pCtagsList, QSplitter::KeepSize); + + // Perform tasks only when the document has been loaded completely + connect(m_pDoc, SIGNAL(completed()), this, SLOT(slotFileOpened())); + + // Be notified when the text in the editor changes + connect(m_pDoc, SIGNAL(textChanged()), this, SLOT(slotSetModified())); + connect(m_pDoc, SIGNAL(undoChanged()), this, SLOT(slotUndoChanged())); + + // Store the sizes of the child windows when the tag list is resized + // (since it may imply a move of the splitter divider) + connect(m_pCtagsList, SIGNAL(resized()), this, SLOT(slotChildResized())); + + // Go to a symbol's line if it is selected in the tag list + connect(m_pCtagsList, SIGNAL(lineRequested(uint)), this, + SLOT(slotGotoLine(uint))); + + // Add Ctag records to the tag list + connect(&m_ctags, SIGNAL(dataReady(FrontendToken*)), m_pCtagsList, + SLOT(slotDataReady(FrontendToken*))); + + // Monitor Ctags' operation + connect(&m_ctags, SIGNAL(finished(uint)), m_pCtagsList, + SLOT(slotCtagsFinished(uint))); + + // Set the context menu + pMenuIf = dynamic_cast<KTextEditor::PopupMenuInterface*>(m_pView); + if (pMenuIf) + pMenuIf->installPopup(pMenu); + + // Emit a signal whenever the cursor's position changes + pCursorIf = dynamic_cast<KTextEditor::ViewCursorInterface*>(m_pView); + if (pCursorIf) { + connect(m_pView, SIGNAL(cursorPositionChanged()), this, + SLOT(slotCursorPosChange())); + } +} + +/** + * Class destructor. + */ +EditorPage::~EditorPage() +{ +} + +/** + * Returns a pointer to the editor document object embedded in this page. + * @returns the document pointer + */ +KTextEditor::Document* EditorPage::getDocument() +{ + return m_pDoc; +} + +/** + * Returns a pointer to the editor view object embedded in this page. + * @returns the view pointer + */ +KTextEditor::View* EditorPage::getView() +{ + return m_pView; +} + +/** + * Returns the full path of the file being edited. + * @return The path of the file associated with the Document object, empty + * string if no file is currently open + */ +QString EditorPage::getFilePath() +{ + return m_pDoc->url().path(); +} + +/** + * Returns the name of the file being edited. + * @return The name of the file associated with the Document object, empty + * string if no file is currently open + */ +QString EditorPage::getFileName() +{ + return m_sName; +} + +/** + * Determines whether this file can be modified, according to the file-system + * permissions, and KScope's global settings. + * @return true if this document can be changed, false otherwise + */ +bool EditorPage::isWritable() +{ + // Check global settings first + if (Config().getReadOnlyMode()) + return false; + + // Return FS write permissions + return m_bWritable; +} + +/** + * Determines if the file edited in this page was modified, and the changes + * were not yet saved. + * @return true if the file was modified, false otherwise + */ +bool EditorPage::isModified() +{ + return m_pDoc->isModified(); +} + +/** + * Opens a file for editing. + * @param sFileName The full path name of the file to edit. + */ +void EditorPage::open(const QString& sFileName) +{ + // Open the given file + m_bOpen = false; + m_pDoc->openURL(sFileName); +} + +/** + * Marks the page as containing a new unnamed file. + */ +void EditorPage::setNewFile() +{ + m_bNewFile = true; + emit newFile(this); +} + +/** + * Saves the edited file. + */ +void EditorPage::save() +{ + if (m_pDoc->isModified()) + m_pDoc->save(); +} + +/** + * Closes an edited file. + * @param bForce true to close the file regardless of any modifications, + * false to prompt the user in case of unsaved chnages + * @return true if the file has been closed, false if the user has aborted + */ +bool EditorPage::close(bool bForce) +{ + QString sPath; + + // To override the prompt-on-close behaviour, we need to mark the file + // as unmodified + if (bForce) + m_pDoc->setModified(false); + + // Close the file, unless the user aborts the action + sPath = m_pDoc->url().path(); + if (!m_pDoc->closeURL()) + return false; + + emit fileClosed(sPath); + return true; +} + +/** + * Applies any changes to the user preferences concerning an editor window. + */ +void EditorPage::applyPrefs() +{ + // Determine whether the editor should work in a read-only mode + if (m_bWritable) + m_pDoc->setReadWrite(!Config().getReadOnlyMode()); + + // Apply preferences to the tag list of this window + m_pCtagsList->applyPrefs(); +} + +/** + * Sets the keyboard focus to the editor part of the page. + * This method is called whenever the page is activated. It is more reasonable + * to set the focus to the editor than to the tag list. + */ +void EditorPage::setEditorFocus() +{ + m_pView->setFocus(); + slotCursorPosChange(); +} + +/** + * Sets the keyboard focus to the tag list. + * This method is called when the "Go To Tag" menu command is invoked. + */ +void EditorPage::setTagListFocus() +{ + m_pCtagsList->slotSetFocus(); +} + +/** + * Sets a bookmark at the given line. + * @param nLine The line to mark + */ +void EditorPage::addBookmark(uint nLine) +{ + KTextEditor::MarkInterface* pMarkIf; + + pMarkIf = dynamic_cast<KTextEditor::MarkInterface*>(m_pDoc); + if (pMarkIf) + pMarkIf->setMark(nLine, KTextEditor::MarkInterface::markType01); +} + +/** + * Retrieves a list of all bookmarks in this page. + */ +void EditorPage::getBookmarks(FileLocationList& fll) +{ + KTextEditor::MarkInterface* pMarkIf; + QPtrList<KTextEditor::Mark> plMarks; + KTextEditor::Mark* pMark; + + // Get the marks interface + pMarkIf = dynamic_cast<KTextEditor::MarkInterface*>(m_pDoc); + if (!pMarkIf) + return; + + // Find all bookmarks + plMarks = pMarkIf->marks(); + for (pMark = plMarks.first(); pMark; pMark = plMarks.next()) { + if (pMark->type == KTextEditor::MarkInterface::markType01) + fll.append(new FileLocation(getFilePath(), pMark->line, 0)); + } +} + +/** + * Returns the currently selected text in an open file. + * @return The selected text, or a null string if no text is currently + * selected + */ +QString EditorPage::getSelection() +{ + KTextEditor::SelectionInterface* pSelect; + + // Get the selected text + pSelect = dynamic_cast<KTextEditor::SelectionInterface*>(m_pDoc); + if (!pSelect || !pSelect->hasSelection()) + return QString::null; + + // Return the selected text + return pSelect->selection(); +} +/** + * Returns a the complete word defined by the current cursor position. + * Attempts to extract a valid C symbol from the location of the cursor, by + * starting at the current line and column, and looking forward and backward + * for non-symbol characters. + * @return A C symbol under the cursor, if any, or QString::null otherwise + */ +QString EditorPage::getWordUnderCursor(uint* pPosInWord) +{ + KTextEditor::ViewCursorInterface* pCursor; + KTextEditor::EditInterface* pEditIf; + QString sLine; + uint nLine, nCol, nFrom, nTo, nLast, nLength; + QChar ch; + + // Get a cursor object + pCursor = dynamic_cast<KTextEditor::ViewCursorInterface*>(m_pView); + if (pCursor == NULL) + return QString::null; + + // Get a pointer to the edit interface + pEditIf = dynamic_cast<KTextEditor::EditInterface*>(m_pDoc); + if (!pEditIf) + return QString::null; + + // Get the line on which the cursor is positioned + pCursor->cursorPositionReal(&nLine, &nCol); + sLine = pEditIf->textLine(nLine); + + // Find the beginning of the current word + for (nFrom = nCol; nFrom > 0;) { + ch = sLine.at(nFrom - 1); + if (!ch.isLetter() && !ch.isDigit() && ch != '_') + break; + + nFrom--; + } + + // Find the end of the current word + nLast = sLine.length(); + for (nTo = nCol; nTo < nLast;) { + ch = sLine.at(nTo); + if (!ch.isLetter() && !ch.isDigit() && ch != '_') + break; + + nTo++; + } + + // Mark empty words + nLength = nTo - nFrom; + if (nLength == 0) + return QString::null; + + // Return the in-word position, if required + if (pPosInWord != NULL) + *pPosInWord = nCol - nFrom; + + // Extract the word under the cursor from the entire line + return sLine.mid(nFrom, nLength); +} + +/** + * Combines getSelection() and getWordUnderCursor() to return a suggested + * text for queries. + * The function first looks if any text is selected. If so, the selected text + * is returned. Otherwise, the word under the cursor location is returned, if + * one exists. + * @return Either the currently selected text, or the word under the cursor, + * or QString::null if both options fail + */ +QString EditorPage::getSuggestedText() +{ + QString sText; + + sText = getSelection(); + if (sText == QString::null) + sText = getWordUnderCursor(); + + return sText; +} + +/** + * Returns the contents of the requested line. + * Truncates the leading and trailing white spaces. + * @param nLine The line number + * @return The text of the requested line, if successful, QString::null + * otherwise + */ +QString EditorPage::getLineContents(uint nLine) +{ + KTextEditor::EditInterface* pEditIf; + QString sLine; + + // Cannot accept line 0 + if (nLine == 0) + return QString::null; + + // Get a pointer to the edit interface + pEditIf = dynamic_cast<KTextEditor::EditInterface*>(m_pDoc); + if (!pEditIf) + return QString::null; + + // Get the line on which the cursor is positioned + sLine = pEditIf->textLine(nLine - 1); + return sLine.stripWhiteSpace(); +} + +/** + * Moves the editing caret to the beginning of a given line. + * @param nLine The line number to move to + */ +void EditorPage::slotGotoLine(uint nLine) +{ + // Ensure there is an open document + if (!m_bOpen) + return; + + // Set the cursor to the requested line + if (!setCursorPos(nLine)) + return; + + // Update Ctags view + m_pCtagsList->gotoLine(nLine); + + // Set the focus to the selected line + m_pView->setFocus(); +} + +/** + * Sets this editor as the current page, when the edited file's name is + * selected in the "Window" menu. + */ +void EditorPage::slotMenuSelect() +{ + m_pParentTab->setCurrentPage(m_pParentTab->indexOf(this)); +} + +/** + * Displays a list of possible completions for the symbol currently under the + * cursor. + */ +void EditorPage::slotCompleteSymbol() +{ + m_pCompletion->slotComplete(); +} + +/** + * Stores the sizes of the child widgets whenever they are changed. + * This slot is connected to the resized() signal of the CtagsList child + * widget. + */ +void EditorPage::slotChildResized() +{ + SPLIT_SIZES si; + + // Only store sizes when allowed to + if (!m_bSaveNewSizes) { + m_bSaveNewSizes = true; + return; + } + + // Get the current widths of the child widgets + si = m_pSplit->sizes(); + if (si.count() == 2) + Config().setEditorSizes(si); +} + +/** + * Sets the visibility status and sizes of the child widgets. + * @param bShowTagList true to show the tag list, false otherwise + * @param si The new sizes to use + */ +void EditorPage::setLayout(bool bShowTagList, const SPLIT_SIZES& si) +{ + // Make sure sizes are not stored during this process + m_bSaveNewSizes = false; + + // Adjust the layout + m_pCtagsList->setShown(bShowTagList); + if (bShowTagList) + m_pSplit->setSizes(si); +} + +/** + * Returns the current position of the cursor. + * @param nLine Holds the line on which the cursor is currently located + * @param nCol Holds the column on which the cursor is currently located + * @return true if successful, false otherwise (cursor interface was not + * obtained) + */ +bool EditorPage::getCursorPos(uint& nLine, uint& nCol) +{ + KTextEditor::ViewCursorInterface* pCursorIf; + + // Acquire the view cursor + pCursorIf = dynamic_cast<KTextEditor::ViewCursorInterface*>(m_pView); + if (pCursorIf == NULL) + return false; + + // Get the cursor position (adjusted to 1-based counting) + pCursorIf->cursorPosition(&nLine, &nCol); + nLine++; + nCol++; + + return true; +} + +/** + * Moves the cursor to a given position. + * @param nLine The cursor's new line number + * @param nCol The cursor's new column number + * @return true if successful, false otherwise (cursor interface was not + * obtained) + */ +bool EditorPage::setCursorPos(uint nLine, uint nCol) +{ + Kate::View* pKateView; + KTextEditor::ViewCursorInterface* pCursorIf; + + // Cannot accept line 0 + if (nLine == 0) + return false; + + // Adjust to 0-based counting + nLine--; + nCol--; + + // Acquire the view cursor + pCursorIf = dynamic_cast<KTextEditor::ViewCursorInterface*>(m_pView); + if (pCursorIf == NULL) + return false; + + // NOTE: The following code is a fix to a bug in Kate, which wrongly + // calculates the column number in setCursorPosition. + pKateView = dynamic_cast<Kate::View*>(m_pView); + if (pKateView != NULL) { + KTextEditor::EditInterface* pEditIf; + const char* szLine; + uint nRealCol; + uint nTabAdjust; + + // Get a pointer to the edit interface + pEditIf = dynamic_cast<KTextEditor::EditInterface*>(m_pDoc); + if (!pEditIf) + return false; + + nRealCol = 0; + + // Check for out of bound line numbers + if (nLine < pEditIf->numLines()) { + // Get the contents of the requested line + szLine = pEditIf->textLine(nLine).latin1(); + + // Check for empty line + if (szLine != NULL) { + // The number of columns which a tab character adds + nTabAdjust = pKateView->tabWidth() - 1; + + // Calculate the real column, based on the tab width + for (; nRealCol < nCol && szLine[nRealCol] != 0; nRealCol++) { + if (szLine[nRealCol] == '\t') + nCol -= nTabAdjust; + } + } + } + else { + // Marker set beyond end of file, move to the last line + nLine = pEditIf->numLines() - 1; + } + // Set the cursor position + pCursorIf->setCursorPositionReal(nLine, nRealCol); + } + else { + // Non-Kate editors: set the cursor position normally + pCursorIf->setCursorPosition(nLine, nCol); + } + + return true; +} + +void EditorPage::setTabWidth(uint nTabWidth) +{ + Kate::Document* pKateDoc; + Kate::Command* pKateCmd; + QString sCmd, sResult; + + pKateDoc = dynamic_cast<Kate::Document*>(m_pDoc); + if ((pKateDoc) && + (pKateCmd = pKateDoc->queryCommand("set-tab-width"))) { + sCmd.sprintf("set-tab-width %u", nTabWidth); + pKateCmd->exec((Kate::View*)m_pView, sCmd, sResult); + } +} + +/** + * Called when a document has completed loading. + * Determines the file's properties and refreshes the tag list of the editor + * window. + * This slot is connected to the completed() signal of the document object. + * The signal is emitted when a new file is opened, or when a modified file is + * saved. + */ +void EditorPage::slotFileOpened() +{ + QFileInfo fi(m_pDoc->url().path()); + + // Get file information + m_sName = fi.fileName(); + m_bWritable = fi.isWritable(); + + // Set read/write or read-only mode + m_pDoc->setReadWrite(!Config().getReadOnlyMode() && m_bWritable); + + // Refresh the tag list + m_pCtagsList->clear(); + m_ctags.run(m_pDoc->url().path()); + + // Check if this is a modified file that has just been saved + if (m_bModified) + emit fileSaved(m_pDoc->url().path(), m_bNewFile); + + // Notify that the document has loaded + m_bOpen = true; + m_bModified = false; + emit fileOpened(this, m_pDoc->url().path()); + + // Set initial position of the cursor + m_nLine = 0; + slotCursorPosChange(); + + // This is no longer a new file + m_bNewFile = false; +} + +/** + * Marks a file as modified when the contents of the editor change. + * This slot is conncted to the textChanged() signal of the Document object. + * In addition to marking the file, this method also emits the modified() + * signal. + */ +void EditorPage::slotSetModified() +{ + // Only perform tasks if the file is not already marked + if (!m_bModified && m_pDoc->isModified()) { + m_bModified = true; + emit modified(this, m_bModified); + +#if KDE_IS_VERSION(3,3,0) + Kate::DocumentExt* pKateDoc; + + // If the editor is a Kate part, check whether it was modified on + // disk as well, and issue a warning if so + pKateDoc = dynamic_cast<Kate::DocumentExt*>(m_pDoc); + if (pKateDoc) + pKateDoc->slotModifiedOnDisk(dynamic_cast<Kate::View*>(m_pView)); +#endif + } + + // Start/restart the auto-completion timer + m_pCompletion->slotAutoComplete(); +} + +/** + * Marks a file as not modified if all undo levels have been applied. + * This slot is conncted to the undoChanged() signal of the Document object. + * In addition to marking the file, this method also emits the modified() + * signal. + */ +void EditorPage::slotUndoChanged() +{ + // Check if the file contents have been fully restored + if (m_bModified && !m_pDoc->isModified()) { + m_bModified = false; + emit modified(this, m_bModified); + } +} + +/** + * Handles changes in the cursor position. + * Emits a signal with the new line and column numbers. + */ +void EditorPage::slotCursorPosChange() +{ + uint nLine, nCol; + + // Find the new line and column number, and emit the signal + if (!getCursorPos(nLine, nCol)) + return; + + emit cursorPosChanged(nLine, nCol); + + // Select the relevant symbol in the tag list + if (Config().getAutoTagHl() && (m_nLine != nLine)) { + m_pCtagsList->gotoLine(nLine); + m_nLine = nLine; + } + + // Abort code completion on cursor changes during the completion + // process + m_pCompletion->abort(); +} + +#include "editorpage.moc" diff --git a/src/editorpage.h b/src/editorpage.h new file mode 100644 index 0000000..0af3aaf --- /dev/null +++ b/src/editorpage.h @@ -0,0 +1,215 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef EDITORPAGE_H +#define EDITORPAGE_H + +#include <qwidget.h> +#include <qhbox.h> +#include <qsplitter.h> +#include <qtabwidget.h> +#include <qpopupmenu.h> +#include <ktexteditor/document.h> +#include <ktexteditor/view.h> +#include <ktexteditor/markinterfaceextension.h> +#include "ctagsfrontend.h" +#include "ctagslist.h" +#include "kscopeconfig.h" +#include "symbolcompletion.h" +#include "projectbase.h" + +/** + * An editor window based on the system's current editing application. + * The page is divided into two panes. One holds an embedded editor, and the + * other holds a list of tags (generated by Ctags) of the file currently being + * edited. + * The widget creates an instance of the editor application, and uses its + * document and view objects that allow KScope to control it. A page also + * Each page is inserted in a separate tab in the EditorTabs widget. + * @author Elad Lahav + */ + +class EditorPage : public QHBox, SymbolCompletion::Interface +{ + Q_OBJECT + +public: + EditorPage(KTextEditor::Document*, QPopupMenu*, QTabWidget* pParent = 0, + const char* szName = 0); + ~EditorPage(); + + void open(const QString&); + void setNewFile(); + void save(); + bool close(bool bForce = false); + void applyPrefs(); + void setEditorFocus(); + void setTagListFocus(); + void addBookmark(uint); + void getBookmarks(FileLocationList&); + + KTextEditor::Document* getDocument(); + KTextEditor::View* getView(); + QString getFilePath(); + QString getFileName(); + bool isWritable(); + bool isModified(); + QString getSelection(); + QString getSuggestedText(); + QString getLineContents(uint); + void setLayout(bool bShowTagList, const SPLIT_SIZES&); + bool getCursorPos(uint&, uint&); + bool setCursorPos(uint, uint nCol = 1); + void setTabWidth(uint); + + virtual QString getWordUnderCursor(uint* pPosInWord = NULL); + + /** + * Implements the SymbolCompletion interface method for returning an + * object that supports KTextEditor::CodeCompletionInterface. + * @return A pointer to the View object of the editor + */ + virtual QObject* getCCObject() { return m_pView; } + + /** + * @return true if a previously unsaved file is currently being edited, + * false otherwise + */ + bool isNewFile() { return m_bNewFile; } + + /** The identifier of the Window menu item which activates this page. */ + int m_nMenuId; + +public slots: + void slotGotoLine(uint); + void slotMenuSelect(); + void slotCompleteSymbol(); + +signals: + /** + * Emitted when a file has been fully loaded into the editor. + * @param pPage The emitting object + * @param sPath The full path of the loaded file + */ + void fileOpened(EditorPage* pPage, const QString& sPath); + + /** + * Emitted when an editor is opened for editing a new file. + * @param pPage The emitting object + */ + void newFile(EditorPage* pPage); + + /** + * Emitted when the 'modified' status of the editor changes. + * This happens when the contents of the editor change, or when the file + * being edited is saved. + * @param pPage The emitting object + * @param bModified true if the new state is 'modified', false if the + * new state is 'unmodified' + */ + void modified(EditorPage* pPage, bool bModified); + + /** + * Emitted when the position of the cursor changes. + * @param nLine The new line number + * @param nCol The new column number + */ + void cursorPosChanged(uint nLine, uint nCol); + + /** + * Emitted when a file is saved after it was modified. + * Indicates the project's cross-reference database needs to be updated. + * @param sPath The full path of the saved file + * @param bIsNew true if this is a new file, false otherwise + */ + void fileSaved(const QString& sPath, bool bIsNew); + + /** + * Emitted when a file is closed. + * @param sPath The full path of the closed file + */ + void fileClosed(const QString& sPath); + +private: + /** The tab widget holding this page. */ + QTabWidget* m_pParentTab; + + /** A Ctags process to use on the edited source file. */ + CtagsFrontend m_ctags; + + /** An adjustable splitter for separating the tag list from the editor + part. */ + QSplitter* m_pSplit; + + /** A list view for displaying Ctags results. */ + CtagsList* m_pCtagsList; + + /** The document part of the editor. */ + KTextEditor::Document* m_pDoc; + + /** The view part of the editor. */ + KTextEditor::View* m_pView; + + /** Whether a source file is currently loaded. */ + bool m_bOpen; + + /** Whether the file being edited is a new one (i.e., never saved + before.) */ + bool m_bNewFile; + + /** The name of the file being edited. */ + QString m_sName; + + /** true if the file system allows this file to be modified, false + otherwise. */ + bool m_bWritable; + + /** This variable is required in addition to m_pDoc->isModified() so + that the modified() signal is emitted only once. */ + bool m_bModified; + + /** The current line position of the cursor. */ + uint m_nLine; + + /** Provides symbol completion. */ + SymbolCompletion* m_pCompletion; + + /** Determines whether size changes in the child widgets should be + stored in the global configuration file. + Needs to be explicitly set to false before _each_ operation that + does not wish to change the defaults. */ + bool m_bSaveNewSizes; + +private slots: + void slotChildResized(); + void slotFileOpened(); + void slotSetModified(); + void slotUndoChanged(); + void slotCursorPosChange(); +}; + +#endif diff --git a/src/editortabs.cpp b/src/editortabs.cpp new file mode 100644 index 0000000..5af9be1 --- /dev/null +++ b/src/editortabs.cpp @@ -0,0 +1,640 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qfileinfo.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kurldrag.h> +#include <kate/document.h> +#include "editortabs.h" +#include "kscopepixmaps.h" +#include "queryview.h" + +/** + * Class constructor. + * @param pParent The parent widget + * @param szName The widget's name + */ +EditorTabs::EditorTabs(QWidget* pParent, const char* szName) : + TabWidget(pParent, szName), + m_pCurPage(NULL), + m_pWindowMenu(NULL), + m_nWindowMenuItems(0), + m_nNewFiles(0) +{ + // Display close buttons + setHoverCloseButton(true); + + // Accept file drops + setAcceptDrops(true); + + // Close an editor page when its close button is clicked + connect(this, SIGNAL(closeRequest(QWidget*)), this, + SLOT(slotRemovePage(QWidget*))); + + // Set an editor page as the active part, when its tab is selected + connect(this, SIGNAL(currentChanged(QWidget*)), this, + SLOT(slotCurrentChanged(QWidget*))); + + // Start dragging a file from a tab + connect(this, SIGNAL(initiateDrag(QWidget*)), this, + SLOT(slotInitiateDrag(QWidget*))); +} + +/** + * Class destructor. + */ +EditorTabs::~EditorTabs() +{ +} + +/** + * @param pWindowMenu Pointer to the main window's "Window" menu (used to + * add an activation menu item for each editor page) + */ +void EditorTabs::setWindowMenu(QPopupMenu* pWindowMenu) +{ + m_pWindowMenu = pWindowMenu; + connect(pWindowMenu, SIGNAL(aboutToShow()), this, + SLOT(slotFillWindowMenu())); + connect(pWindowMenu, SIGNAL(activated(int)), this, + SLOT(slotSetCurrentPage(int))); +} + +/** + * Adds a new editor page to the tab widget. + * @param pNewPage The page to add + */ +void EditorTabs::addEditorPage(EditorPage* pNewPage) +{ + // Create a new tab and set is as the current one + insertTab(pNewPage, ""); + showPage(pNewPage); + + // Add the file edited by this page to the map, and display its name, + // once the file is opened + connect(pNewPage, SIGNAL(fileOpened(EditorPage*, const QString&)), this, + SLOT(slotAttachFile(EditorPage*, const QString&))); + + // Handle new unnamed files + connect(pNewPage, SIGNAL(newFile(EditorPage*)), this, + SLOT(slotNewFile(EditorPage*))); + + // Change tab icon when a file is modified + connect(pNewPage, SIGNAL(modified(EditorPage*, bool)), this, + SLOT(slotFileModified(EditorPage*, bool))); + + // If this is the first page, the current page will not be set by the + // signal handler, so we need to do it manually + if (count() == 1) + slotCurrentChanged(pNewPage); +} + +/** + * Finds and displays a page editing the given file. + * NOTE: The bForceChange parameters is used as a fix for the GUI merge + * problem arising when the found page is the current one. + * @param sFileName The name of the file to search + * @param bForceChange If set to true, the method will emit the signal + * editorChanged() even if the found page is the + * current one + * @return The editor page object, if found, NULL otherwise + */ +EditorPage* EditorTabs::findEditorPage(const QString& sFileName, + bool bForceChange) +{ + EditorMap::iterator itr; + EditorPage* pPage; + bool bEmit; + + // Find the page according to the associated file name + itr = m_mapEdit.find(sFileName); + if (itr == m_mapEdit.end()) + return NULL; + + // Set the page as the current one + pPage = *itr; + bEmit = (bForceChange && (pPage == m_pCurPage)); + showPage(pPage); + + // Emit the editorChanged() signal, if required + if (bEmit) + emit editorChanged(NULL, m_pCurPage); + + return *itr; +} + +/** + * Returns the page associated with the selected tab. + * @return The current editor page + */ +EditorPage* EditorTabs::getCurrentPage() +{ + return (EditorPage*)currentPage(); +} + +/** + * Deletes the currently active page. + * Finds the current page, closes its editor window and deletes the page. + * If other editors are open, another page becomes active. + */ +void EditorTabs::removeCurrentPage() +{ + QWidget* pPage; + + // Get the active page, if any + pPage = currentPage(); + if (pPage == NULL) + return; + + // Close the editor window + removePage(pPage, false); +} + +/** + * Removes all editor pages. + * @return true if successful, false if the user aborts the operation + */ +bool EditorTabs::removeAllPages() +{ + QWidget* pPage; + + // Check if there are any modified files + if (getModifiedFilesCount()) { + // Prompt the user to save these files + switch (KMessageBox::questionYesNoCancel(NULL, + i18n("Some files contain unsaved changes.\nWould you like to " + "save these files?"))) { + case KMessageBox::Yes: + // Save files + slotSaveAll(); + break; + + case KMessageBox::No: + // Close files, ignoring changes + break; + + case KMessageBox::Cancel: + // Abort + return false; + } + } + + // Avoid warning about modification on disk + Kate::Document::setFileChangedDialogsActivated(false); + + // Iterate pages until none is left + while ((pPage = currentPage()) != NULL) + removePage(pPage, true); + + // Restore kate warning if enabled + Kate::Document::setFileChangedDialogsActivated( + Config().getWarnModifiedOnDisk()); + + // All pages were successfully removed + return true; +} + +/** + * Keeps track of the currently active editor page, and notifies on a change + * in the active page. + * This slot is connected to the currentChanged() signal of the QTabWidget + * object. + * @param pWidget The new active page + */ +void EditorTabs::slotCurrentChanged(QWidget* pWidget) +{ + EditorPage* pOldPage; + + // TODO: + // For some reason, this slot is being called twice for every external + // tab activation (e.g., through the Window menu). + // We avoid it, but this really needs to be fixed properly. + if (pWidget == m_pCurPage) + return; + + // Set the new active page + pOldPage = m_pCurPage; + m_pCurPage = (EditorPage*)pWidget; + + if (m_pCurPage) { + // Set the keyboard focus to the editor part of the page + m_pCurPage->setEditorFocus(); + + // Adjust the splitter sizes + m_pCurPage->setLayout(Config().getShowTagList(), + Config().getEditorSizes()); + } + + /* Notify the main window */ + emit editorChanged(pOldPage, m_pCurPage); +} + +/** + * Updates the tab of an editor page to reflect the newly opened file. + * This slot is attached to the fileOpened() signal of an EditorPage object. + * @param pEditPage Pointer to the calling object + * @param sFilePath The full path of the file edited in this page + */ +void EditorTabs::slotAttachFile(EditorPage* pEditPage, + const QString& sFilePath) +{ + // Set the appropriate tab icon, according to the file permissions + if (pEditPage->isWritable()) + setTabIconSet(pEditPage, Pixmaps().getPixmap(KScopePixmaps::TabRW)); + else + setTabIconSet(pEditPage, Pixmaps().getPixmap(KScopePixmaps::TabRO)); + + // Do nothing if the file name has not changed + if (m_mapEdit[sFilePath] == pEditPage) + return; + + // Set the tab caption to the file name, and a tool-tip to the full path + changeTab(pEditPage, pEditPage->getFileName()); + setTabToolTip(pEditPage, sFilePath); + + // Associate the EditorPage object with its file name + m_mapEdit[sFilePath] = pEditPage; +} + +/** + * Marks a page as containing a new unnamed file. + * This slot is attached to the newFile() signal of an EditorPage object. + * @param pEditPage Pointer to the calling object + */ +void EditorTabs::slotNewFile(EditorPage* pEditPage) +{ + QString sCaption; + + // Set the tab caption to mark a new file + m_nNewFiles++; + sCaption = i18n("Untitled ") + QString::number(m_nNewFiles); + changeTab(pEditPage, + Pixmaps().getPixmap(KScopePixmaps::TabRW), + sCaption); + setTabToolTip(pEditPage, i18n("New unsaved file")); +} + +/** + * Applies the user's colour and font preferences to all pages. + */ +void EditorTabs::applyPrefs() +{ + EditorPage* pPage; + int i; + + // Iterate editor pages + for (i = 0; i < count(); i++) { + pPage = (EditorPage*)page(i); + pPage->applyPrefs(); + setTabIconSet(pPage, Pixmaps().getPixmap(pPage->isWritable() ? + KScopePixmaps::TabRW : KScopePixmaps::TabRO)); + } +} + +/** + * Fills a list with the paths and cursor positions of all files currently + * open. + * @param list The list to fill + */ +void EditorTabs::getOpenFiles(FileLocationList& list) +{ + int i; + EditorPage* pPage; + uint nLine, nCol; + + // Iterate over all editor pages + for (i = 0; i < count(); i++) { + // Obtain file and cursor position information + pPage = (EditorPage*)page(i); + if (!pPage->getCursorPos(nLine, nCol)) { + nLine = 1; + nCol = 1; + } + + // Create a new list item + list.append(new FileLocation(pPage->getFilePath(), nLine, nCol)); + } +} + +/** + * Constructs a list bookmarks set to open files. + * Used to store all currently set bookmarks when a session is closed. + * @param fll The list to fill + */ +void EditorTabs::getBookmarks(FileLocationList& fll) +{ + int i; + EditorPage* pPage; + + // Iterate over all editor pages + for (i = 0; i < count(); i++) { + pPage = (EditorPage*)page(i); + pPage->getBookmarks(fll); + } +} + +/** + * Assigns bookmarks to open files. + * Called when a session is opened, to restore any bookmarks set to existing + * editor pages. + * @param fll A list of bookmark positions + */ +void EditorTabs::setBookmarks(FileLocationList& fll) +{ + FileLocation* pLoc; + EditorMap::iterator itr; + EditorPage* pPage; + + // Iterate over the list of bookmarks + for (pLoc = fll.first(); pLoc; pLoc = fll.next()) { + itr = m_mapEdit.find(pLoc->m_sPath); + // Get the relevant page, if any + if (itr != m_mapEdit.end()) { + pPage = *itr; + pPage->addBookmark(pLoc->m_nLine); + } + } +} + +/** + * Fills a QueryView object with the list of currently active bookmarks. + * @param pView The widget to use for displaying bookmarks + */ +void EditorTabs::showBookmarks(QueryView* pView) +{ + int i; + EditorPage* pPage; + FileLocationList fll; + FileLocation* pLoc; + + fll.setAutoDelete(true); + + // Iterate over all editor pages + for (i = 0; i < count(); i++) { + // Obtain file and cursor position information + pPage = (EditorPage*)page(i); + pPage->getBookmarks(fll); + + // Populate the view + for (pLoc = fll.first(); pLoc; pLoc = fll.next()) { + pView->addRecord("", pLoc->m_sPath, + QString::number(pLoc->m_nLine + 1), + pPage->getLineContents(pLoc->m_nLine + 1)); + } + + fll.clear(); + } +} + +/** + * Removes an editor page. + * If there are unsaved changes, the user is prompted, and the file is closed + * according to the user's choice. + * This slot is connected to the clicked() signal of the tab's close button. + * @param pPage The EditorPage object to remove + */ +void EditorTabs::slotRemovePage(QWidget* pPage) +{ + removePage(pPage, false); +} + +/** + * Handles the "View->Show/Hide Tag List" menu item. + * Shows/hides the tag list for the current page, and sets the default values + * for all pages. + */ +void EditorTabs::slotToggleTagList() +{ + EditorPage* pPage; + + // Change the default value + Config().setShowTagList(!Config().getShowTagList()); + + // Apply for the current page, if any + if ((pPage = (EditorPage*)currentPage()) != NULL) { + pPage->setLayout(Config().getShowTagList(), + Config().getEditorSizes()); + } +} + +/** + * Handles drag events over an empty tab widget, or over the tab bar. + * The event is accepted if the dragged object is a list of file paths. + * @param pEvent The drag move event object + */ +void EditorTabs::dragMoveEvent(QDragMoveEvent* pEvent) +{ + KURL::List list; + bool bAccept; + + bAccept = KURLDrag::decode(pEvent, list); + pEvent->accept(bAccept); +} + +/** + * Handles file drops over an empty tab widget, or over the tab bar. + * @param pEvent The drop event object + */ +void EditorTabs::dropEvent(QDropEvent* pEvent) +{ + emit filesDropped(pEvent); +} + +/** + * Called when an editor tab is dragged from the tab widget. + * Initialises the drag operation with a URL that corresponds to the path of + * the file being edited in the corresponding page. + * This slot is connected to the initiateDrag() signal emitted by the tab + * widget. + * @param pWidget The page whose tab is being dragged + */ +void EditorTabs::slotInitiateDrag(QWidget* pWidget) +{ + KURL url; + KURLDrag* pDrag; + + // Create a URL list containing the appropriate file path + url.setPath(((EditorPage*)pWidget)->getFilePath()); + pDrag = new KURLDrag(KURL::List(url), this); + + // Start the drag + pDrag->dragCopy(); +} + +/** + * Changes the tab icon of a modified file. + * @param pEditPage The editor page whose file was modified + * @param bModified true if the file has changed its status to modified, + * false otherwise (i.e., when undo operations restore it + * to its original contents.) + */ +void EditorTabs::slotFileModified(EditorPage* pEditPage, bool bModified) +{ + if (bModified) + setTabIconSet(pEditPage, Pixmaps().getPixmap(KScopePixmaps::TabSave)); + else + setTabIconSet(pEditPage, Pixmaps().getPixmap(KScopePixmaps::TabRW)); +} + +/** + * Counts the number of pages containing modified files. + * @return The number of modified files + */ +int EditorTabs::getModifiedFilesCount() +{ + int i, nResult; + + // Iterate through pages + for (i = 0, nResult = 0; i < count(); i++) { + if (((EditorPage*)page(i))->isModified()) + nResult++; + } + + return nResult; +} + +/** + * Saves all files open for editing. + */ +void EditorTabs::slotSaveAll() +{ + int i; + + // Iterate through pages + for (i = 0; i < count(); i++) + ((EditorPage*)page(i))->save(); +} + +/** + * Selects the page to the left of the current one. + */ +void EditorTabs::slotGoLeft() +{ + int nIndex; + + nIndex = currentPageIndex(); + if (nIndex > 0) { + nIndex--; + setCurrentPage(nIndex); + } +} + +/** + * Selects the page to the right of the current one. + */ +void EditorTabs::slotGoRight() +{ + int nIndex; + + nIndex = currentPageIndex(); + if (nIndex < count() - 1) { + nIndex++; + setCurrentPage(nIndex); + } +} + +/** + * Fills the main window's "Window" menu with the current list of file tabs. + * This slot is attached to the aboutToShow() signal, emitted by the Window + * popup menu. + */ +void EditorTabs::slotFillWindowMenu() +{ + QString sLabel; + int i; + + // Delete old menu items + // NOTE: We can't use aboutToHide() to do that, since it is emitted + // _before_ the activated() signal + for (i = 0; i < m_nWindowMenuItems; i++) + m_pWindowMenu->removeItem(i); + + // Add new items + for (i = 0; i < count(); i++) { + sLabel = (i < 10) ? QString("&%1 %2").arg(i).arg(label(i)) : label(i); + m_pWindowMenu->insertItem(sLabel, i); + } + + // Store the number of items added + m_nWindowMenuItems = i; +} + +/** + * Sets the current page to the given one. + * This slot is attached to the activated() signal, emitted by the "Window" + * popup menu. The tab number to switch to is given by the menu item ID. + * Note that we do not trust setCurrentPage() to filter out the IDs of other + * menu items (which are supposed to be negative numbers). + */ +void EditorTabs::slotSetCurrentPage(int nId) +{ + if (nId >= 0 && nId < count()) + setCurrentPage(nId); +} + +/** + * Closes an edited file, and removes its page. + * Once a file has been closed, its page is removed from the tab widget stack, + * its menu item in the "Windows" menu is deleted and all other references to + * it are removed. + * Note that the operation may fail if the user chooses not to close the file + * when prompted for unsaved changes. + * @param pPage The EditorPage object to remove + * @param bForce true to close the page even if there are unsaved changes, + * false otherwise + * @return true if the page was removed, false otherwise + */ +bool EditorTabs::removePage(QWidget* pPage, bool bForce) +{ + EditorPage* pEditPage; + QString sFilePath; + + // Store the file path for later + pEditPage = (EditorPage*)pPage; + sFilePath = pEditPage->getFilePath(); + + // Close the edited file (may fail if the user aborts the action) + if (!pEditPage->close(bForce)) + return false; + + // Remove the page and all references to it + m_mapEdit.remove(sFilePath); + TabWidget::removePage(pPage); + + // Update the new state if no other page exists (if another page has + // become active, it will update the new state, so there is no need for + // special handling) + if (currentPage() == NULL) + slotCurrentChanged(NULL); + + // Notify the page has been removed + emit editorRemoved(pEditPage); + return true; +} + +#include "editortabs.moc" diff --git a/src/editortabs.h b/src/editortabs.h new file mode 100644 index 0000000..67d0c17 --- /dev/null +++ b/src/editortabs.h @@ -0,0 +1,129 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef EDITORTABS_H +#define EDITORTABS_H + +#include <qwidget.h> +#include <qpopupmenu.h> +#include "tabwidget.h" +#include "editorpage.h" +#include "projectmanager.h" + +typedef QMap<QString, EditorPage*> EditorMap; +class QueryView; + +/** + * A tab widget that holds several editor windows. + * This class provides the main widget in the KScope window. All editors are + * opened as pages of the tab widgets. + * @author Elad Lahav + */ + +class EditorTabs : public TabWidget +{ + Q_OBJECT + +public: + EditorTabs(QWidget* pParent = 0, const char* szName = 0); + ~EditorTabs(); + + void setWindowMenu(QPopupMenu*); + void addEditorPage(EditorPage*); + EditorPage* findEditorPage(const QString&, bool bForceChange = false); + EditorPage* getCurrentPage(); + void removeCurrentPage(); + bool removeAllPages(); + void applyPrefs(); + void getOpenFiles(FileLocationList&); + void getBookmarks(FileLocationList&); + void setBookmarks(FileLocationList&); + void showBookmarks(QueryView*); + +public slots: + void slotRemovePage(QWidget*); + void slotToggleTagList(); + void slotSaveAll(); + void slotGoLeft(); + void slotGoRight(); + +signals: + /** + * Emitted when the current editor page changes. + * @param pOld The previous current page + * @param pNew The new current page + */ + void editorChanged(EditorPage* pOld, EditorPage* pNew); + + /** + * Emitted when an editor page is closed. + * @param pPage The removed page + */ + void editorRemoved(EditorPage* pPage); + + /** + * Indicates that files were dragged and dropped over the tab widget. + * @param pEvent The drop event information + */ + void filesDropped(QDropEvent* pEvent); + +protected: + virtual void dragMoveEvent(QDragMoveEvent*); + virtual void dropEvent(QDropEvent*); + +private: + /** Links a file name with an editor page that has this file open. */ + EditorMap m_mapEdit; + + /** We need to keep track of the current page in order to implement the + editorChanged() signal. */ + EditorPage* m_pCurPage; + + /** A popup menu with Cscope operations for the editor windows. */ + QPopupMenu* m_pWindowMenu; + + /** The number of items added to the window menu (used for removing old + items). */ + int m_nWindowMenuItems; + + /** A counter for creating unique tab captions for new files. */ + int m_nNewFiles; + + int getModifiedFilesCount(); + bool removePage(QWidget*, bool); + +private slots: + void slotCurrentChanged(QWidget*); + void slotAttachFile(EditorPage*, const QString&); + void slotNewFile(EditorPage*); + void slotFileModified(EditorPage*, bool); + void slotInitiateDrag(QWidget*); + void slotFillWindowMenu(); + void slotSetCurrentPage(int); +}; + +#endif diff --git a/src/encoder.cpp b/src/encoder.cpp new file mode 100644 index 0000000..5d04744 --- /dev/null +++ b/src/encoder.cpp @@ -0,0 +1,114 @@ +/*************************************************************************** + * + * Copyright (C) 2006 Elad Lahav (elad_lahav@users.sf.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include "qstring.h" +#include "encoder.h" + +#define CHAR_TO_HEX(c) ((c) < 0xA ? '0' + (c) : ('A' - 0xA) + (c)) +#define HEX_TO_CHAR(h) ((h) > 'A' ? (h) - ('A' - 0xA) : (h) - '0') + +/** + * Class constructor. + */ +Encoder::Encoder() : m_pBuf(NULL), m_nBufLen(0) +{ +} + +/** + * Class destructor. + */ +Encoder::~Encoder() { + if (m_pBuf) + delete[] m_pBuf; +} + +/** + * Encodes a string. + * @param str The string to encode + * @return The hex-encoded ASCII string + */ +const char* Encoder::encode(const QString& str) +{ + const char* szStr; + int nLen, i, j; + + szStr = str.latin1(); + nLen = str.length(); + + // Ensure the buffer is big enough to contain the result + resize((nLen * 2) + 1); + + // Encode the string + for (i = 0, j = 0; i < nLen; i++, j += 2) { + m_pBuf[j] = CHAR_TO_HEX(szStr[i] >> 4); + m_pBuf[j + 1] = CHAR_TO_HEX(szStr[i] & 0x0f); + } + + m_pBuf[j] = 0; + return m_pBuf; +} + +/** + * Decodes a string. + * @param str The string to decode + * @return The decoded string. + */ +const char* Encoder::decode(const QString& str) +{ + const char* szStr; + int nLen, i, j; + + szStr = str.latin1(); + nLen = str.length(); + + // Ensure the buffer is big enough to contain the result + nLen /= 2; + resize(nLen + 1); + + // Decode the string + for (i = 0, j = 0; i < nLen; i++, j += 2) { + m_pBuf[i] = HEX_TO_CHAR(szStr[j]) << 4; + m_pBuf[i] |= HEX_TO_CHAR(szStr[j + 1]); + } + + m_pBuf[i] = 0; + return m_pBuf; +} + +/** + * Sets a new size to the buffer. + * @param nNewLen The new size of the buffer + */ +void Encoder::resize(int nNewLen) +{ + if (m_nBufLen < nNewLen) { + if (m_pBuf) + delete[] m_pBuf; + + m_pBuf = new char[nNewLen]; + } +} diff --git a/src/encoder.h b/src/encoder.h new file mode 100644 index 0000000..2e35cde --- /dev/null +++ b/src/encoder.h @@ -0,0 +1,53 @@ +/*************************************************************************** + * + * Copyright (C) 2006 Elad Lahav (elad_lahav@users.sf.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef __ENCODER_H +#define __ENCODER_H + +/** + * Translates strings to hex-encoded ASCII, and vice-versa. + * @author Elad Lahav + */ +class Encoder +{ +public: + Encoder(); + ~Encoder(); + const char* encode(const QString&); + const char* decode(const QString&); + +private: + /** A buffer to contain the result of encoding/decoding. */ + char* m_pBuf; + + /** The buffer's length. */ + int m_nBufLen; + + void resize(int); +}; + +#endif diff --git a/src/file_ro.png b/src/file_ro.png Binary files differnew file mode 100644 index 0000000..6d0d29d --- /dev/null +++ b/src/file_ro.png diff --git a/src/file_rw.png b/src/file_rw.png Binary files differnew file mode 100644 index 0000000..8312c6b --- /dev/null +++ b/src/file_rw.png diff --git a/src/file_save.png b/src/file_save.png Binary files differnew file mode 100644 index 0000000..41b3f43 --- /dev/null +++ b/src/file_save.png diff --git a/src/filelist.cpp b/src/filelist.cpp new file mode 100644 index 0000000..59492ce --- /dev/null +++ b/src/filelist.cpp @@ -0,0 +1,197 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qlineedit.h> +#include <qfileinfo.h> +#include <klocale.h> +#include "filelist.h" +#include "kscope.h" +#include "kscopeconfig.h" + +/** + * Class constructor. + * @param pParent The parent widget + * @param szName The widget's name + */ +FileList::FileList(QWidget* pParent, const char* szName) : + SearchList(1, pParent, szName), + m_sRoot("/") +{ + // Set the list's columns + m_pList->addColumn(""); + m_pList->addColumn(i18n("File")); + m_pList->addColumn(i18n("Path")); + + // Sort only when asked to by the user + if (Config().getAutoSortFiles()) + m_pList->setSortColumn(1); + else + m_pList->setSortColumn(m_pList->columns() + 1); + + m_pList->setAllColumnsShowFocus(true); + + // Set colours and font + applyPrefs(); +} + +/** + * Class destructor. + */ +FileList::~FileList() +{ +} + +/** + * Adds a single entry to the file list. + * Implements the addItem() virtual method of the FileListTarget base + * class. When a FileList object is given as a parameter to + * ProjectManager::fillList(), this method is called for each file included + * in the project. A new list item is created, containing the file's name and + * path, and is added to the list. + * @param sFilePath The full path of a source file + */ +void FileList::addItem(const QString& sFilePath) +{ + QString sFileType, sFileName, sPath; + int nTypePos; + + // Extract the file name + sFileName = sFilePath.mid(sFilePath.findRev('/') + 1); + + // Get the file's extension (empty string for file names without an + // extension) + nTypePos = sFileName.findRev('.'); + if (nTypePos > -1) + sFileType = sFileName.mid(nTypePos + 1); + + // If a root path has been set, use a $ sign instead of that part of the + // path + sPath = sFilePath; + if (m_sRoot != "/") + sPath.replace(m_sRoot, "$"); + + // Create the list item + new QListViewItem(m_pList, sFileType, sFileName, sPath); +} + +/** + * Searches the list for the given file path. + * @param sPath The full path of the file to find + * @return true if the file was found in the list, false otherwise + */ +bool FileList::findFile(const QString& sPath) +{ + QString sFindPath(sPath); + + if (m_sRoot != "/") + sFindPath.replace(m_sRoot, "$"); + + return (m_pList->findItem(sFindPath, 2) != NULL); +} + +/** + * Removes all items from the file list. + */ +void FileList::clear() +{ + m_pList->clear(); + m_pEdit->setText(""); +} + +/** + * Opens a file for editing when its entry is clicked in the file list. + * @param pItem The clicked list item + */ +void FileList::processItemSelected(QListViewItem* pItem) +{ + QString sPath; + + // Get the file path (replace the root symbol, if required) + sPath = pItem->text(2); + if (sPath.startsWith("$")) + sPath.replace("$", m_sRoot); + + // Submit a request to open the file for editing + emit fileRequested(sPath, 0); +} + +/** + * Sets the list's colours and font, according the user's preferences. + */ +void FileList::applyPrefs() +{ + // Apply colour settings + m_pList->setPaletteBackgroundColor(Config().getColor( + KScopeConfig::FileListBack)); + m_pList->setPaletteForegroundColor(Config().getColor( + KScopeConfig::FileListFore)); + m_pList->setFont(Config().getFont(KScopeConfig::FileList)); +} + +/** + * Associates a root directory with this list. + * For each file in the list, the part of the path corresponding to the root + * is replaced with a $ sign. + * @param sRoot The new root path + */ +void FileList::setRoot(const QString& sRoot) +{ + QListViewItem* pItem; + QString sPath; + + // Update all items in the list + for (pItem = m_pList->firstChild(); pItem != NULL; + pItem = pItem->nextSibling()) { + sPath = pItem->text(2); + + // Restore the full path + sPath.replace("$", m_sRoot); + + // Replace the root with a $ sign + if (sRoot != "/") + sPath.replace(sRoot, "$"); + + pItem->setText(2, sPath); + } + + // Store the new root + m_sRoot = sRoot; +} + +/** + * Constructs a tool-tip for the given item. + * @param pItem The item for which a tip is required + * @param sTip The constructed tip string (on return) + * @return Always true + */ +bool FileList::getTip(QListViewItem* pItem, QString& sTip) +{ + sTip = pItem->text(2); + return true; +} + +#include "filelist.moc" diff --git a/src/filelist.h b/src/filelist.h new file mode 100644 index 0000000..8585dc6 --- /dev/null +++ b/src/filelist.h @@ -0,0 +1,75 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef FILELIST_H +#define FILELIST_H + +#include <qwidget.h> +#include "searchlist.h" +#include "projectmanager.h" + +/** + * Implements a searchable list of files. + * The file list is composed of a list view, and a search box, into which the + * user can enter a file name. The name is matched against the contents of + * the list, and matching items are selected. + * @author Elad Lahav + */ + +class FileList : public SearchList, public FileListTarget +{ + Q_OBJECT + +public: + FileList(QWidget* pParent = 0, const char* szName = 0); + ~FileList(); + + virtual void addItem(const QString&); + bool findFile(const QString&); + void clear(); + void applyPrefs(); + void setRoot(const QString&); + virtual bool getTip(QListViewItem*, QString&); + +signals: + /** + * Emitted when a file is selected, by either double-clicking a list + * item, or by highlighting an item and pressing the ENTER key. + * @param sPath The full path of the selected file + * @param nLine Line number, always set to 0 + */ + void fileRequested(const QString& sPath, uint nLine); + +protected: + virtual void processItemSelected(QListViewItem*); + +private: + /** A common root path for all items in the list. */ + QString m_sRoot; +}; + +#endif diff --git a/src/fileview.cpp b/src/fileview.cpp new file mode 100644 index 0000000..029c20a --- /dev/null +++ b/src/fileview.cpp @@ -0,0 +1,131 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qpushbutton.h> +#include <qfileinfo.h> +#include <qtabwidget.h> +#include <kfiledialog.h> +#include "fileview.h" +#include "filelist.h" +#include "kscopepixmaps.h" + +/** + * Class constructor. + * @param pParent The parent widget + * @param szName The widget's name + * @param fl Widget creation flags + */ +FileView::FileView(QWidget* pParent, const char* szName, WFlags fl) : + FileViewLayout(pParent, szName, fl), + m_pCurBranch(0), + m_sRoot("") +{ + QWidget* pPage; + + // Set the tab widget icons + pPage = m_pTabWidget->page(0); + m_pTabWidget->setTabIconSet(pPage, GET_PIXMAP(TabFileList)); + pPage = m_pTabWidget->page(1); + m_pTabWidget->setTabIconSet(pPage, GET_PIXMAP(TabFileTree)); + + // Create a single column for the file tree + m_pFileTree->addColumn(""); + + // Send the fileRequested() signal whenever a file is selected in either + // the list or the tree + connect(m_pFileList, SIGNAL(fileRequested(const QString&, uint)), this, + SIGNAL(fileRequested(const QString&, uint))); + connect(m_pFileTree, SIGNAL(doubleClicked(QListViewItem*)), + this, SLOT(slotTreeItemSelected(QListViewItem*))); + connect(m_pFileTree, SIGNAL(returnPressed(QListViewItem*)), this, + SLOT(slotTreeItemSelected(QListViewItem*))); +} + +/** + * Class destructor. + */ +FileView::~FileView() +{ +} + +/** + * Sets a new common root path to both the file list and the tree. + * @param sRoot The full path of the new root + */ +void FileView::setRoot(const QString& sRoot) +{ + // Nothing to do if the given root is the same as the old one + if (sRoot == m_sRoot) + return; + + m_sRoot = sRoot; + + // Remove the current branch + if (m_pCurBranch) + m_pFileTree->removeBranch(m_pCurBranch); + + // Update the file list + m_pFileList->setRoot(sRoot); + + // Nothing more to do for an empty root directory + if (sRoot.isEmpty()) + return; + + // Create and open a new branch, with the newly specified root + QFileInfo fi(sRoot); + m_pCurBranch = m_pFileTree->addBranch(KURL(sRoot), fi.fileName()); + m_pCurBranch->setChildRecurse(false); + m_pFileTree->setOpen(m_pCurBranch->root(), true); +} + +/** + * Clears the contents of the file view and file tree. + */ +void FileView::clear() +{ + m_pFileList->clear(); + setRoot(""); +} + +/** + * Emits the fileRequested() signal when a file name is selected in the file + * tree. An item is selected by either double-clicking it or by hittin + * "ENTER" when it is highlighted. + * This slot is connected to the doubleClicked() and returnPressed() signals + * of the KFileTreeView object. + * @param pItem The selected tree item + */ +void FileView::slotTreeItemSelected(QListViewItem* pItem) +{ + KFileTreeViewItem* pTreeItem; + + pTreeItem = (KFileTreeViewItem*)pItem; + if (pTreeItem && !pTreeItem->isDir()) + emit fileRequested(pTreeItem->path(), 0); +} + +#include "fileview.moc" diff --git a/src/fileview.h b/src/fileview.h new file mode 100644 index 0000000..5fc1fe3 --- /dev/null +++ b/src/fileview.h @@ -0,0 +1,80 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef FILEVIEW_H +#define FILEVIEW_H + +#include <kfiletreeview.h> +#include "fileviewlayout.h" + +/** + * A tabbed widget that contains a file list and a file tree. + * The list is an object of type FileList, which displays all files included + * in the current project. The tree is a standard KFileTreeView, which can + * browse through the entire file system. Optionally, the root of the tree + * can be set per project. + * @author Elad Lahav + */ + +class FileView : public FileViewLayout +{ + Q_OBJECT + +public: + FileView(QWidget* pParent = 0, const char* szName = 0, WFlags fl = 0); + ~FileView(); + + /** + * @return The file list widget which is a child of this widget. + */ + FileList* getFileList() { return m_pFileList; } + + void setRoot(const QString&); + void clear(); + +signals: + /** + * Emitted when a file is selected, by either double-clicking a list + * item, or by highlighting an item and pressing the ENTER key. + * @param sPath The full path of the selected file + * @param nLine Line number, always set to 0 + */ + void fileRequested(const QString& sPath, uint nLine); + +private: + /** The current branch in the file tree. */ + KFileTreeBranch* m_pCurBranch; + + /** The current root of the file tree. */ + QString m_sRoot; + +private slots: + void slotTreeItemSelected(QListViewItem*); +}; + +#endif + diff --git a/src/fileviewlayout.ui b/src/fileviewlayout.ui new file mode 100644 index 0000000..24bb1f5 --- /dev/null +++ b/src/fileviewlayout.ui @@ -0,0 +1,136 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>FileViewLayout</class> +<widget class="QWidget"> + <property name="name"> + <cstring>FileViewLayout</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>364</width> + <height>639</height> + </rect> + </property> + <property name="caption"> + <string>Form1</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <widget class="QTabWidget"> + <property name="name"> + <cstring>m_pTabWidget</cstring> + </property> + <property name="toolTip" stdset="0"> + <string></string> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string></string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <widget class="FileList"> + <property name="name"> + <cstring>m_pFileList</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>Project File List</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string></string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <widget class="KFileTreeView"> + <property name="name"> + <cstring>m_pFileTree</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>File Tree</string> + </property> + </widget> + </vbox> + </widget> + </widget> + </vbox> +</widget> +<customwidgets> + <customwidget> + <class>FileList</class> + <header location="local">filelist.h</header> + <sizehint> + <width>-1</width> + <height>-1</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>7</hordata> + <verdata>7</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + </customwidget> + <customwidget> + <class>KFileTreeView</class> + <header location="global">kfiletreeview.h</header> + <sizehint> + <width>-1</width> + <height>-1</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>7</hordata> + <verdata>7</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + </customwidget> +</customwidgets> +<images> + <image name="image0"> + <data format="XBM.GZ" length="79">789c534e494dcbcc4b554829cdcdad8c2fcf4c29c95030e0524611cd48cd4ccf28010a1797249664262b2467241641a592324b8aa363156c15aab914146aadb90067111b1f</data> + </image> +</images> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>filelist.h</includehint> + <includehint>kfiletreeview.h</includehint> +</includehints> +</UI> diff --git a/src/frontend.cpp b/src/frontend.cpp new file mode 100644 index 0000000..fee1c1b --- /dev/null +++ b/src/frontend.cpp @@ -0,0 +1,365 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qfileinfo.h> +#include <qdir.h> +#include <klocale.h> +#include "frontend.h" + +/** + * Class constructor. + * @param nRecordSize The number of fields in each record + * @param bAutoDelete (Optional) true to delete the object when the process + * terminates, false (default) otherwise + */ +Frontend::Frontend(uint nRecordSize, bool bAutoDelete) : KProcess(), + m_nRecords(0), + m_pHeadToken(NULL), + m_pTailToken(NULL), + m_pCurToken(NULL), + m_bAutoDelete(bAutoDelete), + m_bInToken(false), + m_nRecordSize(nRecordSize) +{ + // Parse data on the standard output + connect(this, SIGNAL(receivedStdout(KProcess*, char*, int)), this, + SLOT(slotReadStdout(KProcess*, char*, int))); + + // Parse data on the standard error + connect(this, SIGNAL(receivedStderr(KProcess*, char*, int)), this, + SLOT(slotReadStderr(KProcess*, char*, int))); + + // Delete the process object when the process exits + connect(this, SIGNAL(processExited(KProcess*)), this, + SLOT(slotProcessExit(KProcess*))); +} + +/** + * Class destructor. + */ +Frontend::~Frontend() +{ + // Delete all pending tokens + while (m_pHeadToken) + removeToken(); +} + +/** + * Executes the back-end process. + * @param sName The name of the process (for error messages) + * @param slArgs A list containing the command-line arguments + * @param sWorkDir (Optional) working directory + * @param bBlock (Optional) true to block, false otherwise + * @return true if the process was executed successfully, false otherwise + */ +bool Frontend::run(const QString& sName, const QStringList& slArgs, + const QString& sWorkDir, bool bBlock) +{ + // Cannot start if another controlled process is currently running + if (isRunning()) { + m_sError = i18n("Cannot restart while another process is still " + "running"); + return false; + } + + // Reset variables + m_nRecords = 0; + m_bKilled = false; + + // Setup the command-line arguments + clearArguments(); + *this << slArgs; + + // Set the working directory, if requested + if (!sWorkDir.isEmpty()) + setWorkingDirectory(sWorkDir); + + // Execute the child process + if (!start(bBlock ? KProcess::Block : KProcess::NotifyOnExit, + KProcess::All)) { + m_sError = sName + i18n(": Failed to start process"); + return false; + } + + m_sError = i18n("No error"); + return true; +} + +/** + * Kills the process, and emits the aborted() signal. + * This function should not be called unless the process needs to be + * interrupted. + */ +void Frontend::kill() +{ + m_bKilled = true; + KProcess::kill(); + + emit aborted(); +} + +/** + * Appends a token to the end of the token list. + * @param pToken The token to add + */ +void Frontend::addToken(FrontendToken* pToken) +{ + // Check if this is the firt token + if (m_pHeadToken == NULL) { + m_pHeadToken = pToken; + m_pTailToken = pToken; + } + else { + // Not the first token, append and reset the tail token + m_pTailToken->m_pNext = pToken; + m_pTailToken = pToken; + } +} + +/** + * Removes and deletes the token at the head of the token list. + */ +void Frontend::removeToken() +{ + FrontendToken* pToken; + + if (m_pHeadToken == NULL) + return; + + pToken = m_pHeadToken; + m_pHeadToken = m_pHeadToken->m_pNext; + delete pToken; + + if (m_pHeadToken == NULL) + m_pTailToken = NULL; +} + +/** + * Removes tokens from the head of the list, according to the size of a + * record. + */ +void Frontend::removeRecord() +{ + uint i; + + for (i = 0; (i < m_nRecordSize) && (m_pHeadToken != NULL); i++) + removeToken(); +} + +/** + * Extracts tokens of text out of a given buffer. + * @param ppBuf Points to the buffer to parse, and is set to the + * beginning of the next token, upon return + * @param pBufSize Points to the size of the buffer, and is set to the + * remaining size, upon return + * @param sResult Holds the token's text, upon successful return + * @param delim Holds the delimiter by which the token's end was + * determined + * @return true if a token was extracted up to the given delimter(s), false + * if the buffer ended before a delimiter could be identified + */ +bool Frontend::tokenize(char** ppBuf, int* pBufSize, QString& sResult, + ParserDelim& delim) +{ + int nSize; + char* pBuf; + bool bDelim, bWhiteSpace, bFoundToken = false; + + // Iterate buffer + for (nSize = *pBufSize, pBuf = *ppBuf; (nSize > 0) && !bFoundToken; + nSize--, pBuf++) { + // Test if this is a delimiter character + switch (*pBuf) { + case '\n': + bDelim = ((m_delim & Newline) != 0); + bWhiteSpace = true; + delim = Newline; + break; + + case ' ': + bDelim = ((m_delim & Space) != 0); + bWhiteSpace = true; + delim = Space; + break; + + case '\t': + bDelim = ((m_delim & Tab) != 0); + bWhiteSpace = true; + delim = Tab; + break; + + default: + bDelim = false; + bWhiteSpace = false; + } + + if (m_bInToken && bDelim) { + m_bInToken = false; + *pBuf = 0; + bFoundToken = true; + } + else if (!m_bInToken && !bWhiteSpace) { + m_bInToken = true; + *ppBuf = pBuf; + } + } + + // Either a token was found, or the search through the buffer was + // finished without a delimiter character + if (bFoundToken) { + sResult = *ppBuf; + *ppBuf = pBuf; + *pBufSize = nSize; + } + else if (m_bInToken) { + sResult = QString::fromLatin1(*ppBuf, *pBufSize); + } + else { + sResult = QString::null; + } + + return bFoundToken; +} + +/** + * Handles text sent by the back-end process to the standard error stream. + * By default, this method emits the error() signal with the given text. + * @param sText The text sent to the standard error stream + */ +void Frontend::parseStderr(const QString& sText) +{ + emit error(sText); +} + +/** + * Deletes the process object upon the process' exit. + */ +void Frontend::slotProcessExit(KProcess*) +{ + // Allow specialised clean-up by inheriting classes + finalize(); + + // Signal the process has terminated + emit finished(m_nRecords); + + // Delete the object, if required + if (m_bAutoDelete) + delete this; +} + +/** + * Reads data written on the standard output by the controlled process. + * This is a private slot called attached to the readyReadStdout() signal of + * the controlled process, which means that it is called whenever data is + * ready to be read from the process' stream. + * The method reads whatever data is queued, and sends it to be interpreted + * by parseStdout(). + */ +void Frontend::slotReadStdout(KProcess*, char* pBuffer, int nSize) +{ + char* pLocalBuf; + QString sToken; + bool bTokenEnded; + ParserDelim delim; + + // Do nothing if waiting for process to die + if (m_bKilled) + return; + + pLocalBuf = pBuffer; + + // Iterate over the given buffer + while (nSize > 0) { + // Create a new token, if the last iteration has completed one + if (m_pCurToken == NULL) + m_pCurToken = new FrontendToken(); + + // Extract text until the requested delimiter + bTokenEnded = tokenize(&pLocalBuf, &nSize, sToken, delim); + + // Add the extracted text to the current token + m_pCurToken->m_sData += sToken; + + // If the buffer has ended before the requested delimiter, we need + // to wait for more output from the process + if (!bTokenEnded) + return; + + // Call the process-specific parser function + switch (parseStdout(m_pCurToken->m_sData, delim)) { + case DiscardToken: + // Token should not be saved + delete m_pCurToken; + break; + + case AcceptToken: + // Store token in linked list + addToken(m_pCurToken); + break; + + case RecordReady: + // Store token, and notify the target object that an entry can + // be read + m_nRecords++; + addToken(m_pCurToken); + emit dataReady(m_pHeadToken); + + // Delete all tokens in the entry + removeRecord(); + break; + + case Abort: + kill(); + nSize = 0; + break; + } + + m_pCurToken = NULL; + } +} + +/** + * Reads data written on the standard error by the controlled process. + * This is a private slot called attached to the readyReadStderr() signal of + * the controlled process, which means that it is called whenever data is + * ready to be read from the process' stream. + * The method reads whatever data is queued, and sends it to be interpreted + * by parseStderr(). + */ +void Frontend::slotReadStderr(KProcess*, char* pBuffer, int nSize) +{ + QString sBuf; + + // Do nothing if waiting for process to die + if (m_bKilled) + return; + + sBuf.setLatin1(pBuffer, nSize); + parseStderr(sBuf); +} + +#include "frontend.moc" diff --git a/src/frontend.h b/src/frontend.h new file mode 100644 index 0000000..246128a --- /dev/null +++ b/src/frontend.h @@ -0,0 +1,212 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef FRONTEND_H +#define FRONTEND_H + +#include <qobject.h> +#include <kprocess.h> + + +/** + * Represents a single token in the parsed output stream. + * @author Elad Lahav + */ + +class FrontendToken +{ +public: + /** + * Class constructor. + */ + FrontendToken() : m_pNext(NULL) {} + + /** + * @return The text associated with this token + */ + const QString& getData() const { return m_sData; } + + /** + * @return A pointer to the next token in the strem. + */ + FrontendToken* getNext() const { return m_pNext; } + +protected: + /** Free text associated with the token. */ + QString m_sData; + + /** A pointer to the next token in the stream. */ + FrontendToken* m_pNext; + + friend class Frontend; +}; + +/** + * Abstract base class that provides a front-end to console-based programmes. + * Provides a parsing infrastructure which is based on a list of records, all + * of the same structure. Each record is composed of a number of delimited + * fields (tokens.) + * @author Elad Lahav + */ + +class Frontend : public KProcess +{ + Q_OBJECT + +public: + Frontend(uint, bool bAutoDelete = false); + ~Frontend(); + + virtual bool run(const QString&, const QStringList&, + const QString& sWorkDir = "", bool bBlock = false); + void kill(); + + /** + * @return An string describing the error which made run() fail + */ + const QString& getRunError() { return m_sError; } + +signals: + /** + * Indicates tokens can be read. + * The Frontend object parses the back-end output and creates a list of + * tokens. This signal is emitted when a batch of characters has been + * converted into a token list. + * @param pToken The head of the token list + */ + void dataReady(FrontendToken* pToken); + + /** + * Emitted when the back-end process terminates. + * @param nRecords The number of complete records parsed + */ + void finished(uint nRecords); + + /** + * Indicates that the Cscope process was terminated. + */ + void aborted(); + + /** + * This signal is used to report the progress of the back-end process. + * @param nProgress The current progress value + * @param nTotal The progress value that indicates the process + * is finished + */ + void progress(int nProgress, int nTotal); + + /** + * Emitted when an error message is produced by the back-end process. + */ + void error(const QString& sMsg); + +protected: + /** A set of possible delimiters for parsing process output. */ + enum ParserDelim { Newline = 0x01, Space = 0x02, Tab = 0x04, + WSpace = Space | Tab, All = WSpace | Newline }; + + /** Defines the set of return values for parseStdout(). Determines what + needs to be done with a new token passed to this method. */ + enum ParseResult { + DiscardToken /** Delete this token */, + AcceptToken /** Add this token to the list */, + RecordReady /** This token completes a record */, + Abort /** Kill the process */ + }; + + /** Number of complete records read so far. */ + uint m_nRecords; + + /** The head of the list of parsed output tokens. */ + FrontendToken* m_pHeadToken; + + /** The tail of the list of parsed output tokens. */ + FrontendToken* m_pTailToken; + + /** An iterator on the list of parsed output tokens. */ + FrontendToken* m_pCurToken; + + /** The current delimiters used for parsing the output. */ + ParserDelim m_delim; + + /** An error string produced if run() fails. */ + QString m_sError; + + /** + * Handles a text token received on the Standard Output stream of the + * controlled process. + * This is called by slotReadStdout whenever a new token is recognised. + * Inheriting classes should implement this method to parse the resutling + * stream of tokens. + * @param sToken A part of the text received on the Standard Output, + * disected according to current delimiter settings + * @param delim The delimiter that ended this token + * @result A ParseResult value, indicating what should be done with the + * new token + */ + virtual ParseResult parseStdout(QString& sToken, ParserDelim delim) = 0; + + virtual void parseStderr(const QString&); + + /** + * Called when the process exits. + * Allows inheriting classes to implement process termination handlers. + */ + virtual void finalize() {} + +protected slots: + virtual void slotProcessExit(KProcess*); + +private: + /** Determines whether the object should be deleted once the process has + exited */ + bool m_bAutoDelete; + + /** Determines whether the parser is in the middle of a token, or between + two tokens */ + bool m_bInToken; + + /** The number of fields in each parsed record. Should be defined for + every sub-class. */ + uint m_nRecordSize; + + /** This flag is raised when kill() is called. It signifies that even + though the process may not be dead yet, it should be considered as + such. */ + bool m_bKilled; + + void addToken(FrontendToken*); + void removeToken(); + void removeRecord(); + bool tokenize(char**, int*, QString&, ParserDelim&); + +private slots: + void slotReadStdout(KProcess*, char*, int); + void slotReadStderr(KProcess*, char*, int); +}; + +#endif diff --git a/src/graphedge.cpp b/src/graphedge.cpp new file mode 100644 index 0000000..283a5fe --- /dev/null +++ b/src/graphedge.cpp @@ -0,0 +1,306 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <math.h> +#include <stdlib.h> +#include <qpainter.h> +#include "graphedge.h" +#include "graphnode.h" + +int GraphEdge::RTTI = 1002; + +// Some definitions required by the ConvexHull class +typedef int (*CompFunc)(const void*, const void*); +#define ISLEFT(P0, P1, P2) \ + (P1.x() - P0.x()) * (P2.y() - P0.y()) - \ + (P2.x() - P0.x()) * (P1.y() - P0.y()) +#define FARTHEST(P0, P1, P2) \ + ((P1.x() - P0.x()) * (P1.x() - P0.x()) - \ + (P1.y() - P0.y()) * (P1.y() - P0.y())) - \ + ((P2.x() - P0.x()) * (P2.x() - P0.x()) - \ + (P2.y() - P0.y()) * (P2.y() - P0.y())) + + +/** + * An array of QPoint objects that can compute the convex hull surrounding all + * points in the array. + * @author Elad Lahav + */ +class ConvexHull : public QPointArray +{ +public: + /** + * Class constructor. + */ + ConvexHull() : QPointArray() {} + + /** + * Computes the convex hull of the points stored in the array, using + * Graham's scan. + * @param arrHull Holds the coordinates of the convex hull, upon return + */ + void compute(QPointArray& arrHull) { + uint i, nPivot, nTop; + + // Find the pivot point + nPivot = 0; + for (i = 1; i < size(); i++) { + if ((*this)[i].y() < (*this)[nPivot].y()) { + nPivot = i; + } + else if ((*this)[i].y() == (*this)[nPivot].y() && + (*this)[i].x() < (*this)[nPivot].x()) { + nPivot = i; + } + } + + // Sort points in radial order, relative to the pivot + s_ptPivot = (*this)[nPivot]; + (*this)[nPivot] = (*this)[0]; + (*this)[0] = s_ptPivot; + qsort(&(*this).data()[1], (*this).size() - 1, sizeof(QPoint), + (CompFunc)compRadial); + + // Initialise Graham's scan algorithm + arrHull.resize(size() + 1); + arrHull[0] = (*this)[0]; + arrHull[1] = (*this)[1]; + nTop = 1; + + // Compute the convex hull + for (i = 2; i < size();) { + // TODO: According to the algorithm, the condition should be >0 + // for pushing the point into the stack. For some reason, it works + // only with <0. Why? + if (ISLEFT(arrHull[nTop - 1], arrHull[nTop], (*this)[i]) < 0) { + arrHull[++nTop] = (*this)[i]; + i++; + } + else { + nTop--; + } + } + + // Close the hull + arrHull[++nTop] = (*this)[0]; + arrHull.truncate(nTop + 1); + } + +private: + /** The current pivot point, required by compRadial. */ + static QPoint s_ptPivot; + + /** + * Compares two points based on their angle relative to the current + * pivot point. + * This function is passed as the comparison function of qsort(). + * @param pPt1 A pointer to the first point + * @param pPt2 A pointer to the second point + * @return >0 if the first point is to the left of the second, <0 otherwise + */ + static int compRadial(const QPoint* pPt1, const QPoint* pPt2) { + int nResult; + + // Determine which point is to the left of the other. If the angle is + // the same, the greater point is the one farther from the pivot + nResult = ISLEFT(s_ptPivot, (*pPt1), (*pPt2)); + if (nResult == 0) + return FARTHEST(s_ptPivot, (*pPt1), (*pPt2)); + + return nResult; + } +}; + +QPoint ConvexHull::s_ptPivot; + +/** + * Class constructor. + * @param pCanvas The canvas on which to draw the edge + * @param pHead The edge's starting point + * @param pTail The edge's end point + */ +GraphEdge::GraphEdge(QCanvas* pCanvas, GraphNode* pHead, GraphNode* pTail) : + QCanvasPolygonalItem(pCanvas), + m_pHead(pHead), + m_pTail(pTail), + m_arrPoly(4) +{ +} + +/** + * Class destructor. + */ +GraphEdge::~GraphEdge() +{ + // Classes derived from QCanvasPolygonalItem must call hide() in their + // detructor (according to the Qt documentation) + hide(); +} + +/** + * Calculates the area surrounding the edge, and the bounding rectangle of + * the edge's polygonal head. + * The calculated area is used to find items on the canvas and to display + * tips. The bounding rectangle defines the tip's region (@see QToolTip). + * TODO: The function assumes that the we can simply append the polygon's + * array to that of the splines. Is this always the case, or should we sort + * the points of the polygon in radial order? + * @param arrCurve The control points of the edge's spline + * @param ai Used to calculate the arrow head polygon + */ +void GraphEdge::setPoints(const QPointArray& arrCurve, const ArrowInfo& ai) +{ + ConvexHull ch; + uint i; + int nXpos, nYpos; + + // Redraw an existing edge + if (m_arrArea.size() > 0) + invalidate(); + + // Store the point array for drawing + m_arrCurve = arrCurve; + + // Calculate the arrowhead's polygon + makeArrowhead(ai); + + // Compute the convex hull of the edge + ch.resize(m_arrCurve.size() + m_arrPoly.size() - 2); + ch.putPoints(0, m_arrCurve.size() - 1, m_arrCurve); + ch.putPoints(m_arrCurve.size() - 1, m_arrPoly.size() - 1, m_arrPoly); + ch.compute(m_arrArea); + + // Calculate the head's bounding rectangle + m_rcTip = QRect(m_arrPoly[0], m_arrPoly[0]); + for (i = 1; i < m_arrPoly.size(); i++) { + nXpos = m_arrPoly[i].x(); + if (nXpos < m_rcTip.left()) + m_rcTip.setLeft(nXpos); + else if (nXpos > m_rcTip.right()) + m_rcTip.setRight(nXpos); + + nYpos = m_arrPoly[i].y(); + if (nYpos < m_rcTip.top()) + m_rcTip.setTop(nYpos); + else if (nYpos > m_rcTip.bottom()) + m_rcTip.setBottom(nYpos); + } +} + +/** + * Sets the call information associated with this edge. + * @param sFile The call's file path + * @param sLine The call's line number + * @param sText The call's text + */ +void GraphEdge::setCallInfo(const QString& sFile, const QString& sLine, + const QString& sText) +{ + m_sFile = sFile; + m_sLine = sLine; + m_sText = sText; +} + +/** + * Constructs a tool-tip string for this edge. + * @return The tool-tip text + */ +QString GraphEdge::getTip() const +{ + QString sTip; + + sTip = m_sText + "<br><i>" + m_sFile + "</i>:" + m_sLine; + return sTip; +} + +/** + * Draws the spline as a sequence of cubic Bezier curves. + * @param painter Used for drawing the item on the canvas view + */ +void GraphEdge::drawShape(QPainter& painter) +{ + uint i; + + // Draw the polygon + painter.drawConvexPolygon(m_arrPoly); + + // Draw the Bezier curves + for (i = 0; i < m_arrCurve.size() - 1; i += 3) + painter.drawCubicBezier(m_arrCurve, i); + +#if 0 + // Draws the convex hull of the edge + QPen pen = painter.pen(); + QBrush br = painter.brush(); + painter.setPen(QPen(QColor(255, 0, 0))); + painter.setBrush(QBrush()); + painter.drawPolygon(m_arrArea); + painter.setPen(pen); + painter.setBrush(br); +#endif +} + +/** + * Computes the coordinates of the edge's arrow head, based on its curve. + * @param ai Pre-computed values used for all edges + */ +void GraphEdge::makeArrowhead(const ArrowInfo& ai) +{ + QPoint ptLast, ptPrev; + double dX1, dY1, dX0, dY0, dX, dY, dDeltaX, dDeltaY, dNormLen; + + // The arrowhead follows the line from the second last to the last points + // in the curve + ptLast = m_arrCurve[m_arrCurve.size() - 1]; + ptPrev = m_arrCurve[m_arrCurve.size() - 2]; + + // The first and last points of the polygon are the end of the curve + m_arrPoly.setPoint(0, ptLast.x(), ptLast.y()); + m_arrPoly.setPoint(3, ptLast.x(), ptLast.y()); + + // Convert integer points to double precision values + dX1 = (double)ptLast.x(); + dY1 = (double)ptLast.y(); + dX0 = (double)ptPrev.x(); + dY0 = (double)ptPrev.y(); + + // The values (x1-x0), (y1-y0) and sqrt(1 + tan(theta)^2) are useful + dDeltaX = dX1 - dX0; + dDeltaY = dY1 - dY0; + + // The normalised length of the arrow's sides + dNormLen = ai.m_dLength / sqrt(dDeltaX * dDeltaX + dDeltaY * dDeltaY); + + // Compute the other two points + dX = dX1 - ((dDeltaX - ai.m_dTan * dDeltaY) / ai.m_dSqrt) * dNormLen; + dY = dY1 - ((dDeltaY + ai.m_dTan * dDeltaX) / ai.m_dSqrt) * dNormLen; + m_arrPoly.setPoint(1, (int)dX, (int)dY); + + dX = dX1 - ((dDeltaX + ai.m_dTan * dDeltaY) / ai.m_dSqrt) * dNormLen; + dY = dY1 - ((dDeltaY - ai.m_dTan * dDeltaX) / ai.m_dSqrt) * dNormLen; + m_arrPoly.setPoint(2, (int)dX, (int)dY); +} diff --git a/src/graphedge.h b/src/graphedge.h new file mode 100644 index 0000000..ce399b3 --- /dev/null +++ b/src/graphedge.h @@ -0,0 +1,143 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef GRAPHEDGE_H +#define GRAPHEDGE_H + +#include <qcanvas.h> + +class GraphNode; + +/** + * Information used to draw arrow heads at the end of graph edges. + */ +struct ArrowInfo +{ + /** The length of the arrow. */ + double m_dLength; + + /** The tangent of the arrow's angle from the main line. */ + double m_dTan; + + /** Holds the value sqrt(1 + dTan^2). */ + double m_dSqrt; +}; + +/** + * Draws a directed edge on a canvas. + * The edge is composed of a spline, which is its body, and a polygon, which + * is its head. + * @author Elad Lahav + */ +class GraphEdge : public QCanvasPolygonalItem +{ +public: + GraphEdge(QCanvas*, GraphNode*, GraphNode*); + ~GraphEdge(); + + void setCallInfo(const QString&, const QString&, const QString&); + void setPoints(const QPointArray&, const ArrowInfo&); + QString getTip() const; + + /** + * @return The coordinates of the convex hull surrounding the edge + */ + virtual QPointArray areaPoints() const { return m_arrArea; } + + /** + * @return The head node of the edge + */ + GraphNode* getHead() { return m_pHead; } + + /** + * @return The tail node of the edge + */ + GraphNode* getTail() { return m_pTail; } + + /** + * @return The bounding rectangle of the edge's head + */ + QRect tipRect() const { return m_rcTip; } + + /** + * @return The file path for this call + */ + const QString& getFile() const { return m_sFile; } + + /** + * @return The line number for this call + */ + uint getLine() const { return m_sLine.toUInt(); } + + /** + * @return The call's text + */ + const QString& getText() const { return m_sText; } + + /** Identifies this class among other QCanvasItem classes. */ + static int RTTI; + + /** + * @return The class identifier + */ + virtual int rtti() const { return RTTI; } + +protected: + virtual void drawShape(QPainter&); + +private: + /** The edge's starting point. */ + GraphNode* m_pHead; + + /** The edge's end point. */ + GraphNode* m_pTail; + + /** The points of the polygon part of the edge. */ + QPointArray m_arrPoly; + + /** Control points for the spline part of the edge. */ + QPointArray m_arrCurve; + + QPointArray m_arrArea; + + /** The bounding rectangle of the edge's head, used for displaying the + edge's tool-tip. */ + QRect m_rcTip; + + /** The call's source file. */ + QString m_sFile; + + /** The call's line number. */ + QString m_sLine; + + /** The call's text. */ + QString m_sText; + + void makeArrowhead(const ArrowInfo&); +}; + +#endif diff --git a/src/graphnode.cpp b/src/graphnode.cpp new file mode 100644 index 0000000..28a219d --- /dev/null +++ b/src/graphnode.cpp @@ -0,0 +1,192 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qpainter.h> +#include <qfontmetrics.h> +#include "graphnode.h" + +int GraphNode::RTTI = 1001; + +/** + * Class constructor. + * @param pCanvas The owner canvas + * @param sFunc The node's function + * @param bMultiCall Whether this node represents multiple calls + */ +GraphNode::GraphNode(QCanvas* pCanvas, const QString& sFunc, bool bMultiCall) : + QCanvasPolygon(pCanvas), + m_sFunc(sFunc), + m_bMultiCall(bMultiCall), + m_bDfsFlag(false) +{ + // Every node deletes its out-edges only + m_dictOutEdges.setAutoDelete(true); +} + +/** + * Class destructor. + */ +GraphNode::~GraphNode() +{ +} + +/** + * Finds an edge leaving this node and reaching the given node. + * If such an edge does not exist, a new one is created. + * @param pTail The destination node + * @return The edge that ends at the given node + */ +GraphEdge* GraphNode::addOutEdge(GraphNode* pTail) +{ + GraphEdge* pEdge; + + // Look for the edge + if ((pEdge = m_dictOutEdges.find(pTail->getFunc())) == NULL) { + // Create a new edge + pEdge = new GraphEdge(canvas(), this, pTail); + m_dictOutEdges.insert(pTail->getFunc(), pEdge); + pTail->m_dictInEdges.replace(m_sFunc, pEdge); + } + + // Return the new/constructed edge + return pEdge; +} + +/** + * Performs a weak depth-first-search on the graph. + * The search continues along all edges, both incoming and outgoing. + */ +void GraphNode::dfs() +{ + // Stop if this node is already marked + if (m_bDfsFlag) + return; + + // Mark the node as visited + m_bDfsFlag = true; + + // Continue along outgoing edges + QDictIterator<GraphEdge> itrOut(m_dictOutEdges); + for (; itrOut.current(); ++itrOut) + (*itrOut)->getTail()->dfs(); + + // Continue along incoming edges + QDictIterator<GraphEdge> itrIn(m_dictInEdges); + for (; itrIn.current(); ++itrIn) + (*itrIn)->getHead()->dfs(); +} + +/** + * Deletes all outgoing edges. + * Uses the auto-delete property of the dictionary. + */ +void GraphNode::removeOutEdges() +{ + m_dictOutEdges.clear(); +} + +/** + * Deletes all incoming edges. + * To avoid double deletions, the function lets the head node of the edge remove + * it. + */ +void GraphNode::removeInEdges() +{ + QDictIterator<GraphEdge> itr(m_dictInEdges); + + // Delete edges through their head nodes + for (; itr.current(); ++itr) + (*itr)->getHead()->m_dictOutEdges.remove(m_sFunc); + + // remove edges from the local dictionary (will not delete them) + m_dictInEdges.clear(); +} + +/** + * Returns the first found node connected to this one. + * This function is used with multi-call nodes for retrieving the parent node. + * @param pNode + * @param bCalled + */ +void GraphNode::getFirstNeighbour(GraphNode*& pNode, bool& bCalled) +{ + QDictIterator<GraphEdge> itrIn(m_dictInEdges); + QDictIterator<GraphEdge> itrOut(m_dictOutEdges); + + if (itrIn.current()) { + pNode = itrIn.current()->getHead(); + bCalled = false; + } + else if (itrOut.current()) { + pNode = itrOut.current()->getTail(); + bCalled = true; + } + else { + pNode = NULL; + } +} + +/** + * Modifies the bounding rectangle of the node. + * @param rect The new coordinates to set + */ +void GraphNode::setRect(const QRect& rect) +{ + QPointArray arr(4); + + m_rect = rect; + + arr.setPoint(0, m_rect.topLeft()); + arr.setPoint(1, m_rect.topRight()); + arr.setPoint(2, m_rect.bottomRight()); + arr.setPoint(3, m_rect.bottomLeft()); + setPoints(arr); +} + +/** + * Draws the node. + * @param painter Used for drawing the item on the canvas view + */ +void GraphNode::drawShape(QPainter& painter) +{ + const QPen& pen = painter.pen(); + const QFont& font = painter.font(); + + // Draw the rectangle + painter.setPen(QPen(Qt::black)); + painter.drawRect(m_rect); + + // Draw the text + painter.setPen(pen); + painter.setFont(m_font); + if (m_bMultiCall) + painter.drawText(m_rect, Qt::AlignCenter, "..."); + else + painter.drawText(m_rect, Qt::AlignCenter, m_sFunc); + + painter.setFont(font); +} diff --git a/src/graphnode.h b/src/graphnode.h new file mode 100644 index 0000000..9f48639 --- /dev/null +++ b/src/graphnode.h @@ -0,0 +1,123 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef GRAPHNODE_H +#define GRAPHNODE_H + +#include <qcanvas.h> +#include <qdict.h> +#include "graphedge.h" + +/** + * A canvas item that draws the name of a function insider a filled rectangle. + * This item represents a function in the call graph. + * @author Elad Lahav + */ +class GraphNode : public QCanvasPolygon +{ +public: + GraphNode(QCanvas* pCanvas, const QString&, bool bMultiCall = false); + ~GraphNode(); + + GraphEdge* addOutEdge(GraphNode*); + void dfs(); + void removeOutEdges(); + void removeInEdges(); + void getFirstNeighbour(GraphNode*&, bool&); + + /** + * @param rect The bounding rectangle of the node + */ + void setRect(const QRect& rect); + + /** + * @param font The font to use for drawing the text + */ + void setFont(const QFont& font) { m_font = font; } + + /** + * @return The name of the function + */ + const QString& getFunc() const { return m_sFunc; } + + /** + * @return true for a multiple-call node, false otherwise + */ + bool isMultiCall() { return m_bMultiCall; } + + /** + * @return The set of outgoing edges + */ + QDict<GraphEdge>& getOutEdges() { return m_dictOutEdges; } + + /** + * @return true if this node was already visited during the current DFS, + * false otherwise + */ + bool dfsVisited() { return m_bDfsFlag; } + + /** + * Clears the 'DFS-visited' flag, in preparation for the next DFS. + */ + void dfsReset() { m_bDfsFlag = false; } + + /** Identifies this class among other QCanvasItem classes. */ + static int RTTI; + + /** + * @return The class identifier + */ + virtual int rtti() const { return RTTI; } + +protected: + virtual void drawShape(QPainter&); + +private: + /** Function name. */ + QString m_sFunc; + + /** A list of outgoing edges indexed by destination. */ + QDict<GraphEdge> m_dictOutEdges; + + /** A list of incoming edges indexed by destination. */ + QDict<GraphEdge> m_dictInEdges; + + /** The bounding rectangle for the node. */ + QRect m_rect; + + /** The font to use for drawing the text. */ + QFont m_font; + + /** true for a multiple-call node, false otherwise. */ + bool m_bMultiCall; + + /** Determines whether this node was visited during a depth-first + search. */ + bool m_bDfsFlag; +}; + +#endif diff --git a/src/graphprefdlg.cpp b/src/graphprefdlg.cpp new file mode 100644 index 0000000..c8d381c --- /dev/null +++ b/src/graphprefdlg.cpp @@ -0,0 +1,82 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qspinbox.h> +#include "graphprefdlg.h" +#include "preferencesdlg.h" +#include "kscopeconfig.h" + +/** + * Class constructor. + * @param pParent The parent widget + * @param szName The widget's name + */ +GraphPrefDlg::GraphPrefDlg(QWidget* pParent, const char* szName) : + GraphPrefLayout(pParent, szName, true, 0) +{ + m_pMaxDegSpin->setValue(Config().getGraphMaxNodeDegree()); +} + +/** + * Class destructor. + */ +GraphPrefDlg::~GraphPrefDlg() +{ +} + +/** + * @return The maximal degree value set in the spin box + */ +int GraphPrefDlg::getMaxNodeDegree() +{ + return m_pMaxDegSpin->value(); +} + +/** + * Displays the general preferences dialogue, showing the "Colours" page. + * This slot is connected to the clicked() signal of the colours button. + */ +void GraphPrefDlg::slotFontClicked() +{ + PreferencesDlg dlg(PreferencesDlg::Fonts); + + dlg.exec(); +} + +/** + * Displays the general preferences dialogue, showing the "Fonts" page. + * This slot is connected to the clicked() signal of the fonts button. + */ +void GraphPrefDlg::slotColorClicked() +{ + PreferencesDlg dlg(PreferencesDlg::Colors); + + dlg.exec(); +} + +#include "graphprefdlg.moc" + diff --git a/src/graphprefdlg.h b/src/graphprefdlg.h new file mode 100644 index 0000000..7a4a3c0 --- /dev/null +++ b/src/graphprefdlg.h @@ -0,0 +1,54 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef GRAPHPREFDLG_H +#define GRAPHPREFDLG_H + +#include "graphpreflayout.h" + +/** + * A dialogue that allows the user to configure the appearance and behaviour + * of the call graph. + * @author Elad Lahav + */ +class GraphPrefDlg : public GraphPrefLayout +{ + Q_OBJECT + +public: + GraphPrefDlg(QWidget* pParent = 0, const char* szName = 0); + ~GraphPrefDlg(); + + int getMaxNodeDegree(); + +protected slots: + virtual void slotFontClicked(); + virtual void slotColorClicked(); +}; + +#endif + diff --git a/src/graphpreflayout.ui b/src/graphpreflayout.ui new file mode 100644 index 0000000..a94a206 --- /dev/null +++ b/src/graphpreflayout.ui @@ -0,0 +1,262 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>GraphPrefLayout</class> +<widget class="QDialog"> + <property name="name"> + <cstring>GraphPrefLayout</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>328</width> + <height>164</height> + </rect> + </property> + <property name="caption"> + <string>Call Graph Preferences</string> + </property> + <property name="sizeGripEnabled"> + <bool>true</bool> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout6</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Maximal In/Out Node Degree</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>81</width> + <height>21</height> + </size> + </property> + </spacer> + <widget class="QSpinBox"> + <property name="name"> + <cstring>m_pMaxDegSpin</cstring> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout7</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Colours</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>131</width> + <height>21</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pColorButton</cstring> + </property> + <property name="text"> + <string>...</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout8</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Fonts</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>121</width> + <height>31</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pFontButton</cstring> + </property> + <property name="text"> + <string>...</string> + </property> + </widget> + </hbox> + </widget> + <widget class="Line"> + <property name="name"> + <cstring>line1</cstring> + </property> + <property name="frameShape"> + <enum>HLine</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout1</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <spacer> + <property name="name"> + <cstring>Horizontal Spacing2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>buttonOk</cstring> + </property> + <property name="text"> + <string>&OK</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="autoDefault"> + <bool>true</bool> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>buttonCancel</cstring> + </property> + <property name="text"> + <string>&Cancel</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="autoDefault"> + <bool>true</bool> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<connections> + <connection> + <sender>buttonOk</sender> + <signal>clicked()</signal> + <receiver>GraphPrefLayout</receiver> + <slot>accept()</slot> + </connection> + <connection> + <sender>buttonCancel</sender> + <signal>clicked()</signal> + <receiver>GraphPrefLayout</receiver> + <slot>reject()</slot> + </connection> + <connection> + <sender>m_pColorButton</sender> + <signal>clicked()</signal> + <receiver>GraphPrefLayout</receiver> + <slot>slotColorClicked()</slot> + </connection> + <connection> + <sender>m_pFontButton</sender> + <signal>clicked()</signal> + <receiver>GraphPrefLayout</receiver> + <slot>slotFontClicked()</slot> + </connection> +</connections> +<slots> + <slot access="protected">slotColorClicked()</slot> + <slot access="protected">slotFontClicked()</slot> +</slots> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/src/graphwidget.cpp b/src/graphwidget.cpp new file mode 100644 index 0000000..00ca733 --- /dev/null +++ b/src/graphwidget.cpp @@ -0,0 +1,1162 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <math.h> +#include <stdlib.h> +#include <qfile.h> +#include <qpainter.h> +#include <qtooltip.h> +#include <klocale.h> +#include <kmessagebox.h> +#include "graphwidget.h" +#include "graphnode.h" +#include "graphedge.h" +#include "kscopeconfig.h" +#include "queryviewdlg.h" +#include "encoder.h" +#include "progressdlg.h" + +const char* GRAPH_DIRS[] = { "TB", "LR", "BT", "RL" }; + +const char TMP_TMPL[] = "/tmp/kscope_dot.XXXXXX"; +#define TMP_TMPL_SIZE (sizeof(TMP_TMPL) + 1) + +/** + * Displays a tool tip on the graph. + * Note that we cannot use the standard tool tip class here, since graph + * items are neither rectangular nor is their position known in advance. + * @author Elad Lahav + */ +class GraphTip : public QToolTip +{ +public: + /** + * Class constructor. + * @param pWidget Owner graph widget + */ + GraphTip(GraphWidget* pWidget) : QToolTip(pWidget->viewport()), + m_pGraphWidget(pWidget) {} + + /** + * Class destructor. + */ + virtual ~GraphTip() {} + +protected: + /** + * Called when the pre-conditions for a tool tip are met. + * Asks the owner for a tip to display and, if one is returned, shows + * the tool tip. + * @param ptPos Current mouse position + */ + virtual void maybeTip(const QPoint& ptPos) { + QString sText; + QRect rc; + + // Display a tip, if required by the owner + sText = m_pGraphWidget->getTip(ptPos, rc); + if (sText != QString::null) + tip(rc, sText); + } + +private: + /** The parent graph widget. */ + GraphWidget* m_pGraphWidget; +}; + +/** + * Provides a menu separator with text. + * The separator is added with QMenuData::insertItem(QWidget*). + * @author Elad Lahav + */ +class MenuLabel : public QLabel +{ +public: + /** + * Class constructor. + * @param sText The text to display + * @param pParent The parent widget + */ + MenuLabel(const QString& sText, QWidget* pParent) : + QLabel(sText, pParent) { + // Set the appropriate visual properties + setFrameShape(MenuBarPanel); + setAlignment(AlignHCenter | AlignVCenter); + setIndent(0); + } +}; + +ArrowInfo GraphWidget::s_ai; + +/** + * Class constructor. + * @param pParent The parent widget + * @param szName The widget's name + */ +GraphWidget::GraphWidget(QWidget* pParent, const char* szName) : + QCanvasView(pParent, szName), + m_progress(this), + m_dot(this), + m_dZoom(1.0), + m_nMaxNodeDegree(10), // will be overriden by CallTreeDlg + m_nMultiCallNum(0), + m_pProgressDlg(NULL) +{ + // Automatically delete nodes when they are removed + m_dictNodes.setAutoDelete(true); + + // Create a canvas + setCanvas(new QCanvas(this)); + canvas()->setBackgroundColor(Config().getColor(KScopeConfig::GraphBack)); + + // Create a persistent Cscope process + m_pCscope = new CscopeFrontend(); + + // Add records output by the Cscope process + connect(m_pCscope, SIGNAL(dataReady(FrontendToken*)), this, + SLOT(slotDataReady(FrontendToken*))); + + // Display query progress information + connect(m_pCscope, SIGNAL(progress(int, int)), this, + SLOT(slotProgress(int, int))); + + // Draw the graph when the process has finished + connect(m_pCscope, SIGNAL(finished(uint)), this, + SLOT(slotFinished(uint))); + + // Show a multi-call node when a query results in too many records + connect(m_pCscope, SIGNAL(aborted()), this, + SLOT(slotAborted())); + + // Redraw the graph when Dot exits + connect(&m_dot, SIGNAL(finished(uint)), this, SLOT(slotDotFinished())); + + // Create the node popup menu + m_pNodePopup = new QPopupMenu(this); + + m_pNodePopup->insertItem(new MenuLabel(i18n("<b>Called Functions</b>"), + m_pNodePopup)); + m_pNodePopup->insertItem(i18n("Show"), this, + SLOT(slotShowCalled())); + m_pNodePopup->insertItem(i18n("List/Filter..."), this, + SLOT(slotListCalled())); + m_pNodePopup->insertItem(i18n("Hide"), this, + SLOT(slotHideCalled())); + + m_pNodePopup->insertItem(new MenuLabel(i18n("<b>Calling Functions</b>"), + m_pNodePopup)); + m_pNodePopup->insertItem(i18n("Show"), this, + SLOT(slotShowCalling())); + m_pNodePopup->insertItem(i18n("List/Filter..."), this, + SLOT(slotListCalling())); + m_pNodePopup->insertItem(i18n("Hide"), this, + SLOT(slotHideCalling())); + + m_pNodePopup->insertItem(new MenuLabel(i18n("<b>This Function</b>"), + m_pNodePopup)); + m_pNodePopup->insertItem(i18n("Find Definition"), this, + SLOT(slotFindDef())); + m_pNodePopup->insertItem(i18n("Remove"), this, SLOT(slotRemoveNode())); + + // Create the multi-call node popup menu + m_pMultiCallPopup = new QPopupMenu(this); + m_pMultiCallPopup->insertItem(i18n("List..."), this, + SLOT(slotMultiCallDetails())); + m_pMultiCallPopup->insertSeparator(); + m_pMultiCallPopup->insertItem(i18n("Remove"), this, + SLOT(slotRemoveNode())); + + // Create the edge menu + m_pEdgePopup = new QPopupMenu(this); + m_pEdgePopup->insertItem(i18n("Open Call"), this, SLOT(slotOpenCall())); + + (void)new GraphTip(this); +} + +/** + * Class destructor. + */ +GraphWidget::~GraphWidget() +{ +} + +/** + * Creates a root node for the graph. + * The root node defines the connected component which is always displayed + * (all other connected components are removed when they are no longer + * strongly connected to the root). + * @param sFunc The function name for the root node + */ +void GraphWidget::setRoot(const QString& sFunc) +{ + // Insert a new node to the graph + addNode(sFunc); + draw(); +} + +/** + * Locates a node by its name and, if one does not exist, creates a new node. + * @param sFunc The name of a function + * @return The node corresponding to the given name + */ +GraphNode* GraphWidget::addNode(const QString& sFunc, bool bMultiCall) +{ + GraphNode* pNode; + + // Look for a node with the given name + if ((pNode = m_dictNodes.find(sFunc)) == NULL) { + // Node not found, create it + pNode = new GraphNode(canvas(), sFunc, bMultiCall); + m_dictNodes.insert(sFunc, pNode); + } + + // Return the found/created node + return pNode; +} + +/** + * Adds a call to the graph. + * A call is made between two functions, the caller and the callee. + * @param data Contains information on the call + */ +void GraphWidget::addCall(const CallData& data) +{ + GraphNode* pCaller, * pCallee; + GraphEdge* pEdge; + + // Find the relevant nodes (create new nodes if necessary) + pCaller = addNode(data.m_sCaller); + pCallee = addNode(data.m_sCallee); + + // Create a new edge + pEdge = pCaller->addOutEdge(pCallee); + pEdge->setCallInfo(data.m_sFile, data.m_sLine, data.m_sText); +} + +/** + * Creates a special node representing multiple calls to/from a function. + * Such a node is creates when the number of calls to/from a function exceeds + * a certain number. Thus the graph does not become too cluttered. + * A multiple call node can be replaced by some/all of the actual calls by + * using the "Details..." action in the node's popup menu. + * @param sFunc The parent function + * @param bCalled true if the multiple calls are called from that function, + * false if they are calling the function + */ +void GraphWidget::addMultiCall(const QString& sFunc, bool bCalled) +{ + QString sMulti; + GraphNode* pCaller, * pCallee; + GraphEdge* pEdge; + + // Create a unique name for the new node. + // The name is of the form 0XXX, where XXX is a hexadecimal number. + // We assume that no function starts with a digit, and that there are no + // more than 0xfff multi-call nodes in the graph. + sMulti.sprintf("0%.3x", m_nMultiCallNum); + m_nMultiCallNum = (m_nMultiCallNum + 1) & 0xfff; + + // Find the relevant nodes (create new nodes if necessary) + if (bCalled) { + pCaller = addNode(sFunc); + pCallee = addNode(sMulti, true); + } + else { + pCaller = addNode(sMulti, true); + pCallee = addNode(sFunc); + } + + // Create a new edge + pEdge = pCaller->addOutEdge(pCallee); +} + +/** + * Draws the graph on the canvas using the graphviz engine. + * A new canvas is created, so all items need to be regenerated. + * TODO: Can we use the same canvas and only reposition existing items? + */ +void GraphWidget::draw() +{ + QWMatrix mtx; + char szTempFile[TMP_TMPL_SIZE]; + int nFd; + FILE* pFile; + + // Do nothing if drawing process has already started + if (m_dot.isRunning()) + return; + + // Apply the zoom factor + mtx.scale(m_dZoom, m_dZoom); + setWorldMatrix(mtx); + + // Do not draw until the Dot process finishes + setUpdatesEnabled(false); + + // Open a temporary file + strcpy(szTempFile, TMP_TMPL); + nFd = mkstemp(szTempFile); + if ((pFile = fdopen(nFd, "w")) == NULL) + return; + + // Remember the file name (so it can be deleted later) + m_sDrawFilePath = szTempFile; + + // Write the graph contents to the temporary file + { + QTextStream str(pFile, IO_WriteOnly); + write(str, "graph", "--", false); + } + + // Close the file + fclose(pFile); + + // Draw the graph + if (m_dot.run(szTempFile)) { + // Create the progress dialogue + m_pProgressDlg = new ProgressDlg(i18n("KScope"), + i18n("Generating graph, please wait"), this); + m_pProgressDlg->setMinimumDuration(1000); + m_pProgressDlg->setValue(0); + + // TODO: + // Implement cancel (what do we do when the drawing process is + // terminated, even though the nodes and edges were already added by + // Cscope?) + // m_pProgressDlg->setAllowCancel(true); + } +} + +/** + * Stores a graph on a file. + * The file uses the dot language to describe the graph. + * @param pFile An open file to write to + */ +void GraphWidget::save(FILE* pFile) +{ + // Write the graph using the dot language + QTextStream str(pFile, IO_WriteOnly); + write(str, "digraph", "->", true); +} + +/** + * Exports a graph to a dot file. + * @param sFile The full path of the file to export to + */ +void GraphWidget::save(const QString& sFile) +{ + QFile file(sFile); + + // Open/create the file + if (!file.open(IO_WriteOnly)) + return; + + QTextStream str(&file); + write(str, "digraph", "->", false); +} + +/** + * Changes the zoom factor. + * @param bIn true to zoom in, false to zoom out + */ +void GraphWidget::zoom(bool bIn) +{ + QWMatrix mtx; + + // Set the new zoom factor + if (bIn) + m_dZoom *= 2.0; + else + m_dZoom /= 2.0; + + // Apply the transformation matrix + mtx.scale(m_dZoom, m_dZoom); + setWorldMatrix(mtx); +} + +/** + * Determines the initial zoom factor. + * This method is called from the file parser and therefore does not redraw + * the widget. + * @param dZoom The zoom factor to use + */ +void GraphWidget::setZoom(double dZoom) +{ + m_dZoom = dZoom; +} + +/** + * Changes the graph's direction 90 degrees counter-clockwise. + */ +void GraphWidget::rotate() +{ + QString sDir; + int i; + + // Get the current direction + sDir = Config().getGraphOrientation(); + + // Find the next direction + for (i = 0; i < 4 && sDir != GRAPH_DIRS[i]; i++); + if (i == 4) + i = 0; + else + i = (i + 1) % 4; + + // Set the new direction + sDir = GRAPH_DIRS[i]; + Config().setGraphOrientation(sDir); +} + +/** + * Checks if a tool tip is required for the given position. + * NOTE: We currently return a tool tip for edges only + * @param ptPos The position to query + * @param rc Holds the tip's rectangle, upon return + * @return The tip's text, or QString::null if no tip is required + */ +QString GraphWidget::getTip(const QPoint& ptPos, QRect& rc) +{ + QPoint ptRealPos, ptTopLeft, ptBottomRight; + QCanvasItemList il; + QCanvasItemList::Iterator itr; + GraphEdge* pEdge; + QString sText, sFile, sLine; + + ptRealPos = viewportToContents(ptPos); + ptRealPos /= m_dZoom; + pEdge = NULL; + + // Check if there is an edge at this position + il = canvas()->collisions(ptRealPos); + for (itr = il.begin(); itr != il.end(); ++itr) { + pEdge = dynamic_cast<GraphEdge*>(*itr); + if (pEdge != NULL) + break; + } + + // No tip if no edge was found + if (pEdge == NULL) + return QString::null; + + // Set the rectangle for the tip (the tip is closed when the mouse leaves + // this area) + rc = pEdge->tipRect(); + ptTopLeft = rc.topLeft(); + ptBottomRight = rc.bottomRight(); + ptTopLeft *= m_dZoom; + ptBottomRight *= m_dZoom; + ptTopLeft = contentsToViewport(ptTopLeft); + ptBottomRight = contentsToViewport(ptBottomRight); + rc = QRect(ptTopLeft, ptBottomRight); + + // Create a tip for this edge + return pEdge->getTip(); +} + +/** + * Resizes the canvas. + * @param nWidth The new width + * @param nHiehgt The new height + */ +void GraphWidget::resize(int nWidth, int nHeight) +{ + canvas()->resize(nWidth + 2, nHeight + 2); +} + +/** + * Displays a node on the canvas. + * Sets the parameters used for drawing the node on the canvas. + * @param sFunc The function corresponding to the node to draw + * @param rect The coordinates of the node's rectangle + */ +void GraphWidget::drawNode(const QString& sFunc, const QRect& rect) +{ + GraphNode* pNode; + + // Find the node + pNode = addNode(sFunc); + + // Set the visual aspects of the node + pNode->setRect(rect); + pNode->setZ(2.0); + pNode->setPen(QPen(Qt::black)); + pNode->setFont(Config().getFont(KScopeConfig::Graph)); + + if (pNode->isMultiCall()) + pNode->setBrush(Config().getColor(KScopeConfig::GraphMultiCall)); + else + pNode->setBrush(Config().getColor(KScopeConfig::GraphNode)); + + // Draw the node + pNode->show(); +} + +/** + * Displays an edge on the canvas. + * Sets the parameters used for drawing the edge on the canvas. + * @param sCaller Identifies the edge's head node + * @param sCallee Identifies the edge's tail node + * @param arrCurve Control points for the edge's spline + */ +void GraphWidget::drawEdge(const QString& sCaller, const QString& sCallee, + const QPointArray& arrCurve) +{ + GraphNode* pCaller, * pCallee; + GraphEdge* pEdge; + + // Find the edge + pCaller = addNode(sCaller); + pCallee = addNode(sCallee); + pEdge = pCaller->addOutEdge(pCallee); + + // Set the visual aspects of the edge + pEdge->setPoints(arrCurve, s_ai); + pEdge->setZ(1.0); + pEdge->setPen(QPen(Qt::black)); + pEdge->setBrush(QBrush(Qt::black)); + + // Draw the edge + pEdge->show(); +} + +#define PI 3.14159265 + +/** + * Sets and computes values used for drawing arrows. + * Initialises the static ArroInfo structure, which is passed in drawEdge(). + * @param nLength The arrow head length + * @param nDegrees The angle, in degrees, between the base line and each + * of the arrow head's sides + */ +void GraphWidget::setArrowInfo(int nLength, int nDegrees) +{ + double dRad; + + // Turn degrees into radians + dRad = ((double)nDegrees) * PI / 180.0; + + s_ai.m_dLength = (double)nLength; + s_ai.m_dTan = tan(dRad); + s_ai.m_dSqrt = sqrt(1 + s_ai.m_dTan * s_ai.m_dTan); +} + +/** + * Draws the contents of the canvas on this view. + * NOTE: This method is overriden to fix a strange bug in Qt that leaves + * a border around the canvas part of the view. It should be deleted once + * this bug is fixed. + * TODO: Is there a better way of erasing the border? + * @param pPainter Used to paint on the view + * @param nX The horizontal origin of the area to draw + * @param nY The vertical origin of the area to draw + * @param nWidth The width of the area to draw + * @param nHeight The height of the area to draw + */ +void GraphWidget::drawContents(QPainter* pPainter, int nX, int nY, + int nWidth, int nHeight) +{ + // Draw the contents of the canvas + QCanvasView::drawContents(pPainter, nX, nY, nWidth, nHeight); + + // Erase the canvas's area border + if (canvas() != NULL) { + QRect rect = canvas()->rect(); + pPainter->setBrush(QBrush()); // Null brush + pPainter->setPen(Config().getColor(KScopeConfig::GraphBack)); + pPainter->drawRect(-1, -1, rect.width() + 2, rect.height() + 2); + } +} + +/** + * Handles mouse clicks over the graph view. + * @param pEvent Includes information on the mouse press event + */ +void GraphWidget::contentsMousePressEvent(QMouseEvent* pEvent) +{ + QPoint ptRealPos; + QCanvasItemList il; + QCanvasItemList::Iterator itr; + QString sFunc; + GraphNode* pNode; + GraphEdge* pEdge; + + pNode = NULL; + pEdge = NULL; + + // Handle right-clicks only + if (pEvent->button() != Qt::RightButton) { + QCanvasView::contentsMousePressEvent(pEvent); + return; + } + + // Take the zoom factor into consideration + ptRealPos = pEvent->pos(); + ptRealPos /= m_dZoom; + + // Check if an item was clicked + il = canvas()->collisions(ptRealPos); + for (itr = il.begin(); itr != il.end(); ++itr) { + if (dynamic_cast<GraphNode*>(*itr) != NULL) + pNode = dynamic_cast<GraphNode*>(*itr); + else if (dynamic_cast<GraphEdge*>(*itr) != NULL) + pEdge = dynamic_cast<GraphEdge*>(*itr); + } + + // Handle clicks over different types of items + if (pNode != NULL) { + // Show a context menu for nodes + showNodeMenu(pNode, pEvent->globalPos()); + } + else if (pEdge != NULL) { + // Show a context menu for edges + showEdgeMenu(pEdge, pEvent->globalPos()); + } + else { + // Take the default action + QCanvasView::contentsMousePressEvent(pEvent); + } +} + +/** + * Writes a description of the graph to the given stream, using the Dot + * language. + * The method allows for both directed graphs and non-directed graphs, the + * latter are required for drawing purposes (since Dot will not produce the + * arrow heads and the splines terminate before reaching the nodes). + * @param str The stream to write to + * @param sType Either "graph" or "digraph" + * @param sEdge The edge connector ("--" or "->") + * @param bWriteCall true to write call information, false otherwise + */ +void GraphWidget::write(QTextStream& str, const QString& sType, + const QString& sEdge, bool bWriteCall) +{ + QFont font; + QDictIterator<GraphNode> itr(m_dictNodes); + GraphEdge* pEdge; + Encoder enc; + + font = Config().getFont(KScopeConfig::Graph); + + // Header + str << sType << " G {\n"; + + // Graph attributes + str << "\tgraph [rankdir=" << Config().getGraphOrientation() << ", " + << "kscope_zoom=" << m_dZoom + << "];\n"; + + // Default node attributes + str << "\tnode [shape=box, height=\"0.01\", style=filled, " + << "fillcolor=\"" << Config().getColor(KScopeConfig::GraphNode).name() + << "\", " + << "fontcolor=\"" << Config().getColor(KScopeConfig::GraphText).name() + << "\", " + << "fontname=\"" << font.family() << "\", " + << "fontsize=" << QString::number(font.pointSize()) + << "];\n"; + + // Iterate over all nodes + for (; itr.current(); ++itr) { + // Write a node + str << "\t" << itr.current()->getFunc() << ";\n"; + + // Iterate over all edges leaving this node + QDictIterator<GraphEdge> itrEdge(itr.current()->getOutEdges()); + for (; itrEdge.current(); ++itrEdge) { + pEdge = itrEdge.current(); + str << "\t" << pEdge->getHead()->getFunc() << sEdge + << pEdge->getTail()->getFunc(); + + // Write call information + if (bWriteCall) { + str << " [" + << "kscope_file=\"" << pEdge->getFile() << "\"," + << "kscope_line=" << pEdge->getLine() << "," + << "kscope_text=\"" << enc.encode(pEdge->getText()) << "\"" + << "]"; + } + + str << ";\n"; + } + } + + // Close the graph + str << "}\n"; +} + +/** + * Removes all edges attached to a function node at the given direction. + * Any strongly connected components that are no longer connected to that + * function are deleted. + * @param pNode The node for which to remove the edges + * @param bOut true for outgoing edges, false for incoming + */ +void GraphWidget::removeEdges(GraphNode* pNode, bool bOut) +{ + // Remove the edges + if (bOut) + pNode->removeOutEdges(); + else + pNode->removeInEdges(); + + // Remove all graph components no longer connected to this one + removeDisconnected(pNode); +} + +/** + * Removes all edges and nodes that are not weakly connected to the given node. + * This function is called to clean up the graph after edges were removed from + * the given node. + * @param pNode The node to which all other nodes have to be connected + */ +void GraphWidget::removeDisconnected(GraphNode* pNode) +{ + QDictIterator<GraphNode> itr(m_dictNodes); + + // Find all weakly connected components attached to this node + pNode->dfs(); + + // Delete all unmarked nodes, reset marked ones + while (itr.current()) { + if (!(*itr)->dfsVisited()) { + m_dictNodes.remove((*itr)->getFunc()); + } + else { + (*itr)->dfsReset(); + ++itr; + } + } +} + +/** + * Shows a popup menu for a node. + * This menu is shown after a node has been right-clicked. + * @param pNode The node for which to show the menu + * @param ptPos The position of the menu + */ +void GraphWidget::showNodeMenu(GraphNode* pNode, const QPoint& ptPos) +{ + // Remember the node + m_pMenuItem = pNode; + + // Show the popup menu. + if (pNode->isMultiCall()) + m_pMultiCallPopup->popup(ptPos); + else + m_pNodePopup->popup(ptPos); +} + +/** + * Shows a popup menu for an edge. + * This menu is shown after an edge has been right-clicked. + * @param pEdge The edge for which to show the menu + * @param ptPos The position of the menu + */ +void GraphWidget::showEdgeMenu(GraphEdge* pEdge, const QPoint& ptPos) +{ + // Remember the edge + m_pMenuItem = pEdge; + + // Show the popup menu. + m_pEdgePopup->popup(ptPos); +} + +/** + * Redraws the widget when new instructions are available. + * This slot is connected to the finished() signal emitted by the dot front- + * end. + */ +void GraphWidget::slotDotFinished() +{ + // Delete the temporary file + if (m_sDrawFilePath != "") { + QFile::remove(m_sDrawFilePath); + m_sDrawFilePath = ""; + } + + // Delete the progress dialogue + if (m_pProgressDlg) { + delete m_pProgressDlg; + m_pProgressDlg = NULL; + } + + setUpdatesEnabled(true); + canvas()->update(); +} + +/** + * Adds an entry to the tree, as the child of the active item. + * Called by a CscopeFrontend object, when a new entry was received in its + * whole from the Cscope back-end process. The entry contains the data of a + * function calling the function described by the active item. + * @param pToken The first token in the entry + */ +void GraphWidget::slotDataReady(FrontendToken* pToken) +{ + CallData data; + QString sFunc; + + // Get the file name + data.m_sFile = pToken->getData(); + pToken = pToken->getNext(); + + // Get the function name + sFunc = pToken->getData(); + pToken = pToken->getNext(); + + // Get the line number (do not accept global information on a call tree) + data.m_sLine = pToken->getData(); + if (data.m_sLine.toUInt() == 0) + return; + + pToken = pToken->getNext(); + + // Get the line's text + data.m_sText = pToken->getData(); + + // Determine the caller and the callee + if (m_bCalled) { + data.m_sCaller = m_sQueriedFunc; + data.m_sCallee = sFunc; + } + else { + data.m_sCaller = sFunc; + data.m_sCallee = m_sQueriedFunc; + } + + // Add the call to the graph + addCall(data); +} + +/** + * Displays search progress information. + * This slot is connected to the progress() signal emitted by a + * CscopeFrontend object. + * @param nProgress The current progress value + * @param nTotal The expected final value + */ +void GraphWidget::slotProgress(int nProgress, int nTotal) +{ + m_progress.setProgress(nProgress, nTotal); +} + +/** + * Disables the expandability feature of an item, if no functions calling it + * were found. + * @param nRecords Number of records reported by the query + */ +void GraphWidget::slotFinished(uint /*nRecords*/) +{ + // Destroy the progress bar + m_progress.finished(); + + // Redraw the graph + draw(); +} + +/** + * Adds a multiple call node when a query results in too many entries. + * This slot is attached to the aborted() signal of the Cscope process. + */ +void GraphWidget::slotAborted() +{ + KMessageBox::information(this, i18n("The query produced too many results.\n" + "A multiple-call node will appear in the graph instead.\n" + "Hint: The maximum number of in/out edges\n" + "can be adjusted by clicking the dialogue's \"Preferences\" button")); + + addMultiCall(m_sQueriedFunc, m_bCalled); + draw(); +} + +/** + * Shows functions called from the current function node. + * This slot is connected to the "Show Called Functions" popup menu + * action. + */ +void GraphWidget::slotShowCalled() +{ + GraphNode* pNode; + + // Make sure the menu item is a node + pNode = dynamic_cast<GraphNode*>(m_pMenuItem); + if (pNode == NULL) + return; + + // Run a query for called functions + m_sQueriedFunc = pNode->getFunc(); + m_bCalled = true; + m_pCscope->query(CscopeFrontend::Called, m_sQueriedFunc, true, + Config().getGraphMaxNodeDegree()); +} + +/** + * Shows a list of function calls from the current node. + * The list is displayed in a query dialogue. The user can the select which + * calls should be displayed in the graph. + * This slot is connected to the "List Called Functions" popup menu + * action. + */ +void GraphWidget::slotListCalled() +{ + GraphNode* pNode; + + // Make sure the menu item is a node + pNode = dynamic_cast<GraphNode*>(m_pMenuItem); + if (pNode == NULL) + return; + + QueryViewDlg dlg(0, (QWidget*)parent()); + + // Show the query view dialogue + dlg.query(CscopeFrontend::Called, pNode->getFunc()); + if (dlg.exec() != QDialog::Accepted) + return; + + // The OK button was clicked, replace current calls with the listed ones + pNode->removeOutEdges(); + + QueryView::Iterator itr; + CallData data; + + data.m_sCaller = pNode->getFunc(); + + // Add all listed calls + for (itr = dlg.getIterator(); !itr.isEOF(); itr.next()) { + data.m_sCallee = itr.getFunc(); + data.m_sFile = itr.getFile(); + data.m_sLine = itr.getLine(); + data.m_sText = itr.getText(); + + addCall(data); + } + + // Clean-up and redraw the graph + removeDisconnected(pNode); + draw(); +} + +/** + * Hides functions called from the current function node. + * This slot is connected to the "Hide Called Functions" popup menu + * action. + */ +void GraphWidget::slotHideCalled() +{ + GraphNode* pNode; + + // Make sure the menu item is a node + pNode = dynamic_cast<GraphNode*>(m_pMenuItem); + if (pNode == NULL) + return; + + // Remove edges and redraw the graph + removeEdges(pNode, true); + draw(); +} + +/** + * Shows functions calling tothe current function node. + * This slot is connected to the "Show Calling Functions" popup menu + * action. + */ +void GraphWidget::slotShowCalling() +{ + GraphNode* pNode; + + // Make sure the menu item is a node + pNode = dynamic_cast<GraphNode*>(m_pMenuItem); + if (pNode == NULL) + return; + + // Run a query for called functions + m_sQueriedFunc = pNode->getFunc(); + m_bCalled = false; + m_pCscope->query(CscopeFrontend::Calling, m_sQueriedFunc, true, + Config().getGraphMaxNodeDegree()); +} + +/** + * Shows a list of function calls to the current node. + * The list is displayed in a query dialogue. The user can the select which + * calls should be displayed in the graph. + * This slot is connected to the "List Calling Functions" popup menu + * action. + */ +void GraphWidget::slotListCalling() +{ + GraphNode* pNode; + + // Make sure the menu item is a node + pNode = dynamic_cast<GraphNode*>(m_pMenuItem); + if (pNode == NULL) + return; + + QueryViewDlg dlg(0, (QWidget*)parent()); + + // Show the query view dialogue + dlg.query(CscopeFrontend::Calling, pNode->getFunc()); + if (dlg.exec() != QDialog::Accepted) + return; + + // The OK button was clicked, replace current calls with the listed ones + pNode->removeInEdges(); + + QueryView::Iterator itr; + CallData data; + + data.m_sCallee = pNode->getFunc(); + + // Add all listed calls + for (itr = dlg.getIterator(); !itr.isEOF(); itr.next()) { + data.m_sCaller = itr.getFunc(); + data.m_sFile = itr.getFile(); + data.m_sLine = itr.getLine(); + data.m_sText = itr.getText(); + + addCall(data); + } + + // Clean-up and redraw the graph + removeDisconnected(pNode); + draw(); +} + +/** + * Hides functions calling to the current function node. + * This slot is connected to the "Hide CallingFunctions" popup menu + * action. + */ +void GraphWidget::slotHideCalling() +{ + GraphNode* pNode; + + // Make sure the menu item is a node + pNode = dynamic_cast<GraphNode*>(m_pMenuItem); + if (pNode == NULL) + return; + + // Remove edges and redraw the graph + removeEdges(pNode, false); + draw(); +} + +/** + * Looks up the definition of the current function node. + * This slot is connected to the "Find Definition" popup menu action. + */ +void GraphWidget::slotFindDef() +{ + GraphNode* pNode; + QueryViewDlg* pDlg; + + // Make sure the menu item is a node + pNode = dynamic_cast<GraphNode*>(m_pMenuItem); + if (pNode == NULL) + return; + + // Create a query view dialogue + pDlg = new QueryViewDlg(QueryViewDlg::DestroyOnSelect, this); + + // Display a line when it is selected in the dialogue + connect(pDlg, SIGNAL(lineRequested(const QString&, uint)), this, + SIGNAL(lineRequested(const QString&, uint))); + + // Start the query + pDlg->query(CscopeFrontend::Definition, pNode->getFunc()); +} + +/** + * Deletes a node from the graph (alogn with all edges connected to this + * node). + * The node removed is the one over which the context menu was invoked. + * This slot is connected to the "Remove" popup menu action. + */ +void GraphWidget::slotRemoveNode() +{ + GraphNode* pNode; + + // Make sure the menu item is a node + pNode = dynamic_cast<GraphNode*>(m_pMenuItem); + if (pNode == NULL) + return; + + // Remove all incoming edges + pNode->removeInEdges(); + + // Remove the node (also deletes the object and its outgoing edges) + m_dictNodes.remove(pNode->getFunc()); + + // Redraw the graph + draw(); +} + +/** + * Shows the list of calls that is represented by a single multi-call node. + * This slot handles the "Details..." command of the multi-call node menu. + */ +void GraphWidget::slotMultiCallDetails() +{ + GraphNode* pNode, * pParent; + bool bCalled; + + // Make sure the menu item is a node + pNode = dynamic_cast<GraphNode*>(m_pMenuItem); + if (pNode == NULL || !pNode->isMultiCall()) + return; + + // Get the required information from the node + pNode->getFirstNeighbour(pParent, bCalled); + if (!pParent) + return; + + QueryViewDlg dlg(0, (QWidget*)parent()); + + dlg.query(bCalled ? CscopeFrontend::Calling : CscopeFrontend::Called, + pParent->getFunc()); + dlg.exec(); +} + +/** + * Emits a signal to open an editor at the file and line matching the call + * information of the current edge. + * This slot is connected to the "Open Call" popup menu action (for edges). + */ +void GraphWidget::slotOpenCall() +{ + GraphEdge* pEdge; + QString sFile, sLine; + + // Make sure the menu item is an edge + pEdge = dynamic_cast<GraphEdge*>(m_pMenuItem); + if (pEdge != NULL && pEdge->getFile() != "") + emit lineRequested(pEdge->getFile(), pEdge->getLine()); +} + +#include "graphwidget.moc" diff --git a/src/graphwidget.h b/src/graphwidget.h new file mode 100644 index 0000000..119ddbb --- /dev/null +++ b/src/graphwidget.h @@ -0,0 +1,213 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef GRAPHWIDGET_H +#define GRAPHWIDGET_H + +#include <qcanvas.h> +#include <qpopupmenu.h> +#include <qdict.h> +#include "cscopefrontend.h" +#include "graphnode.h" +#include "dotfrontend.h" + +class ProgressDlg; + +/** + * A widget that displays call tree graphs generated by graphviz. + * This class is based on a QCanvasView widget, and displays two types of + * canvas items: GraphNode, which draws the name of a function inside a + * polygon, and ArrowEdge, which is a directed graph edge shaped like an + * arrow. + * The call tree graph is populated using the addNode() method. The first + * call creates a root node. Subsequent calls add nodes which represent either + * functions called by a previously inserted node, or that are calling such + * a node. A directed edge is created to depict the relationship between a + * node and its parent. + * Drawing is done through the embedded Agraph_t object (a graph, as defined + * by the graphviz libraray). When the draw() method is called, the graph is + * layed out in memory. The class then uses the CodeGenerator class to + * obtain a set of instructions on how to actually draw the graph. These + * instructions are used to create the appropriate canvas items. + * An application _must_ call GraphWidget::init() before attempting to use + * this class. It should also call GraphWidget::fini() when it no longer needs + * any of these widgets. + * @author Elad Lahav + */ +class GraphWidget : public QCanvasView +{ + Q_OBJECT + +public: + GraphWidget(QWidget* pParent = 0, const char* szName = 0); + ~GraphWidget(); + + /** + * Information on a function call, as produced by a Cscope query. + * This structure is used for adding calls to the graph. + * @see addCall() + */ + struct CallData + { + /** The name of the calling function. */ + QString m_sCaller; + + /** The name of the called function. */ + QString m_sCallee; + + /** Path of the file in which the call appears. */ + QString m_sFile; + + /** The line number of the call. */ + QString m_sLine; + + /** The call's text. */ + QString m_sText; + }; + + /** Graph orientation values. */ + enum Orientation { Portrait, Landscape }; + + void setRoot(const QString&); + GraphNode* addNode(const QString&, bool bMultiCall = false); + void addCall(const CallData&); + void addMultiCall(const QString&, bool); + void draw(); + void save(FILE*); + void save(const QString&); + void zoom(bool); + void setZoom(double); + void rotate(); + QString getTip(const QPoint&, QRect&); + + void resize(int, int); + void drawNode(const QString&, const QRect&); + void drawEdge(const QString&, const QString&, const QPointArray&); + + /** + * Adjusts the maximal number of calling/called functions shown for + * every node (@see m_nMaxNodeDegree). + * @param nMaxNodeDegree The new value to set + */ + void setMaxNodeDegree(int nMaxNodeDegree) { m_nMaxNodeDegree = + nMaxNodeDegree; } + + static void setArrowInfo(int, int); + +signals: + /** + * Emitted when the user makes a request to view the contents of a + * location in the source code. + * This can be the location of a call, the definition of a function, + * etc. + * @param sPath The full path of the file to show + * @param nLine The line number in this file + */ + void lineRequested(const QString& sPath, uint nLine); + +protected: + virtual void drawContents(QPainter*, int, int, int, int); + virtual void contentsMousePressEvent(QMouseEvent*); + +private: + /** The graph is stored as a map of nodes indexed by their names. + Each node holds a list of outgoing edges. */ + QDict<GraphNode> m_dictNodes; + + /** A Cscope process to use for running queries. */ + CscopeFrontend* m_pCscope; + + /** Displays query progress information. */ + CscopeProgress m_progress; + + /** A Dot process used to draw the graph. */ + DotFrontend m_dot; + + /** Remembers the function the was last queried for calling/called + functions. */ + QString m_sQueriedFunc; + + /** Remembers whether the last query was for calling or called + functions. */ + bool m_bCalled; + + /** The node over which the popup menu has been invoked. */ + QCanvasPolygonalItem* m_pMenuItem; + + /** A popup menu that appears when a node is right-clicked. */ + QPopupMenu* m_pNodePopup; + + /** A popup menu that appears when a node is right-clicked. */ + QPopupMenu* m_pMultiCallPopup; + + /** A popup menu that appears when an edge is right-clicked. */ + QPopupMenu* m_pEdgePopup; + + /** The zoom factor for the graph. */ + double m_dZoom; + + /** Maximal number of in/out edges per node. If this number is exceeded, + the graph shows a single "multi-call" node. */ + int m_nMaxNodeDegree; + + /** Holds information used to draw arrow heads. */ + static ArrowInfo s_ai; + + /** Used for generating unique names for multi-call nodes. */ + uint m_nMultiCallNum; + + /** Holds the path of the temporary dot file used for drawing the graph. */ + QString m_sDrawFilePath; + + /** Allows lengthy drawing operations to be cancelled. */ + ProgressDlg* m_pProgressDlg; + + void write(QTextStream&, const QString&, const QString&, bool); + void removeEdges(GraphNode*, bool); + void removeDisconnected(GraphNode*); + void showNodeMenu(GraphNode*, const QPoint&); + void showEdgeMenu(GraphEdge*, const QPoint&); + +private slots: + void slotDotFinished(); + void slotDataReady(FrontendToken*); + void slotProgress(int, int); + void slotFinished(uint); + void slotAborted(); + void slotShowCalled(); + void slotListCalled(); + void slotHideCalled(); + void slotShowCalling(); + void slotListCalling(); + void slotHideCalling(); + void slotFindDef(); + void slotRemoveNode(); + void slotMultiCallDetails(); + void slotOpenCall(); +}; + +#endif diff --git a/src/hi16-app-kscope.png b/src/hi16-app-kscope.png Binary files differnew file mode 100644 index 0000000..7659966 --- /dev/null +++ b/src/hi16-app-kscope.png diff --git a/src/hi32-app-kscope.png b/src/hi32-app-kscope.png Binary files differnew file mode 100644 index 0000000..54d2af0 --- /dev/null +++ b/src/hi32-app-kscope.png diff --git a/src/historypage.cpp b/src/historypage.cpp new file mode 100644 index 0000000..eabd6b6 --- /dev/null +++ b/src/historypage.cpp @@ -0,0 +1,124 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <klocale.h> +#include "historypage.h" +#include "historyview.h" + +int HistoryPage::s_nMaxPageID = 0; + +/** + * Class constructor. + * @param pParent The parent widget + * @param szName The widget's name + */ +HistoryPage::HistoryPage(QWidget* pParent, const char* szName) : + QueryPageBase(pParent, szName), + m_nPageID(++s_nMaxPageID) +{ + m_pView = new HistoryView(this); + + connect(m_pView, SIGNAL(lineRequested(const QString&, uint)), this, + SIGNAL(lineRequested(const QString&, uint))); + + // Set colours and font + applyPrefs(); +} + +/** + * Class destructor. + */ +HistoryPage::~HistoryPage() +{ + if (s_nMaxPageID == m_nPageID) + s_nMaxPageID--; +} + +/** + * Creates a new position history record at the top of the list. + * @param sFile The file name associated with the record + * @param nLine The line number + * @param sText The text of the file at the given line + */ +void HistoryPage::addRecord(const QString& sFile, uint nLine, + const QString& sText) +{ + HistoryItem* pItem, * pNextItem; + + pItem = (HistoryItem*)m_pView->currentItem(); + if (pItem != NULL) { + // Do not add duplicate items + if ((pItem->text(1) == sFile) && (pItem->text(2).toUInt() == nLine)) + return; + + // Remove all items above the current one, so the new item is added to + // the top of the list + pItem = pItem->m_pPrevSibling; + while (pItem != NULL) { + pNextItem = pItem; + pItem = pItem->m_pPrevSibling; + delete pNextItem; + } + } + + // Create the new item at the top of the list + m_pView->addRecord("", sFile, QString::number(nLine), sText, NULL); +} + +/** + * Creates a new history item. + * This version is used when history records are read from a file. + * @param sFile The file name + * @param sLine The line number + * @param sText The contents of the line + */ +void HistoryPage::addRecord(const QString&, const QString& sFile, + const QString& sLine, const QString& sText) +{ + m_pView->addRecord("", sFile, sLine, sText, NULL); +} + +/** + * Creates a tab caption for this page, based on the unique page ID. + * @param bBrief true to use brief caption names, false otherwise + */ +QString HistoryPage::getCaption(bool bBrief) const +{ + return (bBrief ? QString(i18n("HIS ")) : QString(i18n("History "))) + + QString::number(m_nPageID); +} + +/** + * Creates a unique file name for saving the contents of the history page. + * @return The unique file name to use + */ +QString HistoryPage::getFileName(const QString&) const +{ + return QString("History_") + QString::number(m_nPageID); +} + +#include "historypage.moc" diff --git a/src/historypage.h b/src/historypage.h new file mode 100644 index 0000000..9ff614f --- /dev/null +++ b/src/historypage.h @@ -0,0 +1,72 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef HISTORYPAGE_H +#define HISTORYPAGE_H + +#include "querypagebase.h" + +/** + * A QueryWidget page for holding position history. + * @author Elad Lahav + */ +class HistoryPage : public QueryPageBase +{ + Q_OBJECT + +public: + HistoryPage(QWidget* pParent = 0, const char* szName = 0); + ~HistoryPage(); + + void addRecord(const QString&, uint, const QString&); + + virtual QString getCaption(bool bBrief = false) const; + +protected: + virtual void addRecord(const QString&, const QString&, const QString&, + const QString&); + virtual QString getFileName(const QString&) const; + + /** + * @return Always true, since History files do not contain a header + */ + virtual bool readHeader(QTextStream&) { return true; } + + /** + * This method does nothing, since History files do not contain a header. + */ + virtual void writeHeader(QTextStream&) {} + +private: + /** A unique ID used to create a tab caption for this page. */ + int m_nPageID; + + /** Used to generate the unique page ID for each object. */ + static int s_nMaxPageID; +}; + +#endif diff --git a/src/historyview.cpp b/src/historyview.cpp new file mode 100644 index 0000000..519c4dc --- /dev/null +++ b/src/historyview.cpp @@ -0,0 +1,124 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include "historyview.h" + +/** + * Class constructor. + * @param pParent The parent widget + * @param szName The widget's name + */ +HistoryView::HistoryView(QWidget* pParent, const char* szName) : + QueryView(pParent, szName) +{ + // Disable sorting + setSortColumn(-1); + + // Don't show the "Function" column + setColumnWidthMode(0, QListView::Manual); + setColumnWidth(0, 0); +} + +/** + * Class destructor. + */ +HistoryView::~HistoryView() +{ +} + +/** + * Creates a new list item showing a history record. + * @param sFunc The name of the function + * @param sFile The file path + * @param sLine The line number in the above file + * @param sText The line's text + */ +void HistoryView::addRecord(const QString& /* sFunc */, const QString& sFile, + const QString& sLine, const QString& sText, QListViewItem*) +{ + HistoryItem* pItem; + + pItem = new HistoryItem(this, sFile, sLine, sText); + setSelected(pItem, true); +} + +/** + * Moves to the previous item in the history, selecting it for display. + * Note that this function move to the item which chronologically precedes + * the current one, which, in list view terms, is the next item. + */ +void HistoryView::selectPrev() +{ + QListViewItem* pItem; + + // Get the current item + pItem = currentItem(); + + // Select the next item in the list + if (pItem != NULL && pItem->nextSibling() != NULL) { + pItem = pItem->nextSibling(); + select(pItem); + } +} + +/** + * Moves to the next item in the history, selecting it for display. + * Note that this function move to the item which chronologically succedes + * the current one, which, in list view terms, is the previous item. + */ +void HistoryView::selectNext() +{ + HistoryItem* pItem; + + // Get the current item + pItem = (HistoryItem*)currentItem(); + + // Select the previous item in the list + if (pItem != NULL && pItem->m_pPrevSibling != NULL) { + pItem = pItem->m_pPrevSibling; + select(pItem); + } +} + +/** + * Deletes the item on which a popup-menu has been invoked. + * This slot is connected to the remove() signal of the QueryResultsMenu + * object. + * @param pItem The item to remove + */ +void HistoryView::slotRemoveItem(QListViewItem* pItem) +{ + HistoryItem* pCurItem, * pNextItem; + + pCurItem = (HistoryItem*)pItem; + if ((pNextItem = (HistoryItem*)pCurItem->nextSibling()) != NULL) + pNextItem->m_pPrevSibling = pCurItem->m_pPrevSibling; + + delete pCurItem; +} + +#include "historyview.moc" diff --git a/src/historyview.h b/src/historyview.h new file mode 100644 index 0000000..38a0c46 --- /dev/null +++ b/src/historyview.h @@ -0,0 +1,91 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef HISTORYVIEW_H +#define HISTORYVIEW_H + +#include "queryview.h" + +/** + * A list view item for holding position history records. + * A QListViewItem cannot reference its preceding item, which is required to + * create a stack-like history list. Therefore a HistoryItem object + * stores a pointer to the item just above it in the list. The pointer is + * persistent, since the list cannot be sorted. + * @author Elad Lahav + */ + +class HistoryItem : public QListViewItem +{ +public: + /** + * Class constructor. + * @param pList The parent list view + * @param sFile The file path in this record + * @param sLine The line number + * @param sText The contents of the line in the given file + */ + HistoryItem(QListView* pList, QString sFile, QString sLine, + QString sText) : + QListViewItem(pList, "", sFile, sLine, sText), + m_pPrevSibling(NULL) { + HistoryItem* pNext; + + // Mark the new item as the predecessor of its next sibling + if ((pNext = (HistoryItem*)nextSibling()) != NULL) + pNext->m_pPrevSibling = this; + } + + /** The item immediately above this one in the list. */ + HistoryItem* m_pPrevSibling; +}; + +/** + * A list view widget for holding position history. + * Position history is kept in a stack-like list. Positions are always added + * to the top of the list, immediately before the current item. If the + * current item is not the top one, all items above it are purged first. + * To keep the stack-like structure, the list cannot be sorted. + * @author Elad Lahav + */ +class HistoryView : public QueryView +{ +Q_OBJECT +public: + HistoryView(QWidget* pParent = 0, const char* szName = 0); + ~HistoryView(); + + virtual void addRecord(const QString&, const QString&, const QString&, + const QString&, QListViewItem*); + virtual void selectNext(); + virtual void selectPrev(); + +protected slots: + virtual void slotRemoveItem(QListViewItem*); +}; + +#endif diff --git a/src/kscope.cpp b/src/kscope.cpp new file mode 100644 index 0000000..4a4ab60 --- /dev/null +++ b/src/kscope.cpp @@ -0,0 +1,1754 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qfile.h> +#include <kfiledialog.h> +#include <kmenubar.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <klineedit.h> +#include <kinputdialog.h> +#include <kxmlguifactory.h> +#include <kstatusbar.h> +#include <kurldrag.h> +#include <kkeydialog.h> +#include "kscope.h" +#include "kscopeconfig.h" +#include "projectmanager.h" +#include "editortabs.h" +#include "fileview.h" +#include "filelist.h" +#include "querywidget.h" +#include "editormanager.h" +#include "cscopefrontend.h" +#include "ctagslist.h" +#include "newprojectdlg.h" +#include "openprojectdlg.h" +#include "preferencesdlg.h" +#include "dirscanner.h" +#include "querypage.h" +#include "calltreedlg.h" +#include "calltreemanager.h" +#include "kscopepixmaps.h" +#include "progressdlg.h" +#include "projectfilesdlg.h" +#include "cscopemsgdlg.h" +#include "symboldlg.h" +#include "symbolcompletion.h" +#include "queryviewdlg.h" +#include "graphwidget.h" +#include "makedlg.h" +#include "welcomedlg.h" +#include "bookmarksdlg.h" +#include "kscopeactions.h" + +/** + * Class constructor. + * @param pParent The parent widget + * @param szName The widget's name + */ +KScope::KScope(QWidget* pParent, const char* szName) : + KParts::DockMainWindow(pParent, szName), + m_pCscopeBuild(NULL), + m_sCurFilePath(""), + m_nCurLine(0), + m_pProgressDlg(NULL), + m_bUpdateGUI(true), + m_bCscopeVerified(false), + m_bRebuildDB(false), + m_pMakeDlg(NULL) +{ + QString sPath; + + // Load configuration + Config().load(); + + // Create the main child widgets + m_pEditTabs = new EditorTabs(this, NULL); + m_pQueryWidget = new QueryWidget(this); + m_pFileView = new FileView(this); + m_pFileList = m_pFileView->getFileList(); + m_pMsgDlg = new CscopeMsgDlg(this); + m_pQueryDock = createDockWidget("Query Window", QPixmap()); + m_pFileViewDock = createDockWidget("File List Window", QPixmap()); + + // Connect menu and toolbar items with the object's slots + m_pActions = new KScopeActions(this); + m_pActions->init(); + m_pActions->slotEnableProjectActions(false); + + // Show a toolbar show/hide menu + setStandardToolBarMenuEnabled(true); + + // Create the initial GUI (no active part) + setXMLFile("kscopeui.rc"); + createShellGUI(); + + // Create all child widgets + initMainWindow(); + + // Create control objects + m_pProjMgr = new ProjectManager(); + m_pEditMgr = new EditorManager(this); + m_pCallTreeMgr = new CallTreeManager(this); + + // Initialise the icon manager + Pixmaps().init(); + + // Open a file for editing when selected in the project's file list or the + // file tree + connect(m_pFileView, SIGNAL(fileRequested(const QString&, uint)), this, + SLOT(slotShowEditor(const QString&, uint))); + + // Delete an editor page object after it is removed + connect(m_pEditTabs, SIGNAL(editorRemoved(EditorPage*)), + this, SLOT(slotDeleteEditor(EditorPage*))); + + connect(m_pEditTabs, SIGNAL(filesDropped(QDropEvent*)), this, + SLOT(slotDropEvent(QDropEvent*))); + + // Set an editor as the active part whenever its owner tab is selected + connect(m_pEditTabs, SIGNAL(editorChanged(EditorPage*, EditorPage*)), + this, SLOT(slotChangeEditor(EditorPage*, EditorPage*))); + + // Display a file at a specific line when selected in a query list + connect(m_pQueryWidget, SIGNAL(lineRequested(const QString&, uint)), + this, SLOT(slotQueryShowEditor(const QString&, uint))); + + // Display the symbol dialogue when the user opens a new query page + connect(m_pQueryWidget, SIGNAL(newQuery()), + this, SLOT(slotQueryReference())); + + // Rebuild the project database after a certain time period has elapsed + // since the last save + connect(&m_timerRebuild, SIGNAL(timeout()), this, SLOT(slotRebuildDB())); + + // Display a file at a specific line when selected in a call tree dialogue + connect(m_pCallTreeMgr, SIGNAL(lineRequested(const QString&, uint)), + this, SLOT(slotQueryShowEditor(const QString&, uint))); + + // Store main window settings when closed + setAutoSaveSettings(); + + // Initialise arrow head drawing + GraphWidget::setArrowInfo(20, 15); + + // Use a maximised window the first time + if (Config().isFirstTime()) + showMaximized(); + + // Show the Welcome message + if (Config().showWelcomeDlg()) { + show(); + slotShowWelcome(); + } + + // If this is the first time the user has launched KScope, prompt him/her + // to configure the global parameters + if (Config().isFirstTime()) + slotConfigure(); +} + +/** + * Class destructor. + */ +KScope::~KScope() +{ + // Save configuration + Config().store(); + Config().storeWorkspace(this); + + delete m_pCallTreeMgr; + delete m_pEditMgr; + delete m_pCscopeBuild; + delete m_pProjMgr; + + if (m_pMakeDlg != NULL) + delete m_pMakeDlg; +} + +/** + * Positions child widgets into their docking stations, and performs some + * other main window initialisation. + */ +void KScope::initMainWindow() +{ + KStatusBar* pStatus; + KDockWidget* pMainDock; + QPopupMenu* pPopup; + + // Create the status bar + pStatus = statusBar(); + pStatus->insertItem(i18n(" Line: N/A Col: N/A "), 0, 0, true); + + // Create the main dock for the editor tabs widget + pMainDock = createDockWidget("Editors Window", QPixmap()); + pMainDock->setWidget(m_pEditTabs); + pMainDock->setDockSite(KDockWidget::DockCorner); + setMainDockWidget(pMainDock); + setView(pMainDock); + pMainDock->setEnableDocking(KDockWidget::DockNone); + + // Create the query window dock + m_pQueryDock->setWidget(m_pQueryWidget); + m_pQueryDock->manualDock(pMainDock, KDockWidget::DockBottom, 65); + + // Update the relevant shell action when the dock is hidden through its + // close button + connect(m_pQueryDock, SIGNAL(headerCloseButtonClicked()), m_pActions, + SLOT(slotQueryDockClosed())); + + // Create the file view dock + m_pFileViewDock->setWidget(m_pFileView); + m_pFileViewDock->manualDock(pMainDock, KDockWidget::DockRight, 80); + + // Update the relevant shell action when the dock is hidden through its + // close button + connect(m_pFileViewDock, SIGNAL(headerCloseButtonClicked()), m_pActions, + SLOT(slotFileViewDockClosed())); + + // Associate the "Window" menu with the editor tabs widdget + pPopup = (QPopupMenu*)factory()->container("window", this); + m_pEditTabs->setWindowMenu(pPopup); + + // Associate the "Query" popup menu with the query widget + pPopup = (QPopupMenu*)factory()->container("query_popup", this); + m_pQueryWidget->setPageMenu(pPopup, m_pActions->getLockAction()); + + // Restore dock configuration + Config().loadWorkspace(this); + m_bHideQueryOnSelection = m_pQueryDock->isHidden(); + m_pActions->initLayoutActions(); +} + +/** + * Handles the "File->Quit" command. Closes the main window, which terminates + * the application. + */ +void KScope::slotClose() +{ + // Destroy the main window + KParts::DockMainWindow::close(); +} + +/** + * Called when a request has been issued to close the main window. + * Tries to close the active project. + * @return true if the main window can be closed, false otherwise + */ +bool KScope::queryClose() +{ + bool bResult; + + m_bUpdateGUI = false; + bResult = slotCloseProject(); + m_bUpdateGUI = true; + + return bResult; +} + +/** + * Handles the "Project->New..." command. + * Prompts the user for the name and folder for the project, and then creates + * the project. + */ +void KScope::slotCreateProject() +{ + NewProjectDlg dlg(true, this); + ProjectBase::Options opt; + QString sProjPath; + + // Prompt the user to close any active projects + if (m_pProjMgr->curProject()) { + if (KMessageBox::questionYesNo(0, + i18n("The current project needs to be closed before a new one is" + " created.\nWould you like to close it now?")) != + KMessageBox::Yes) { + return; + } + + // Try to close the project. + if (!slotCloseProject()) + return; + } + + // Display the "New Project" dialog + if (dlg.exec() != QDialog::Accepted) + return; + + // Create and open the new project + dlg.getOptions(opt); + if (m_pProjMgr->create(dlg.getName(), dlg.getPath(), opt, sProjPath)) + openProject(sProjPath); +} + +/** + * Handles the "Project->Open..." command. + * Prompts the user for a project file ("cscope.proj"), and opens the + * selected project. + */ +void KScope::slotOpenProject() +{ + OpenProjectDlg dlg; + QString sPath; + + if (dlg.exec() == QDialog::Rejected) + return; + + sPath = dlg.getPath(); + + // Check if the path refers to a permanent or temporary project + if (QFileInfo(sPath).isDir()) + openProject(sPath); + else + openCscopeOut(sPath); +} + +/** + * Handles the "Project->Add/Remove Files..." command. + * Opens the project's files dialog, which allows the user to add and remove + * source files. + */ +void KScope::slotProjectFiles() +{ + ProjectBase* pProj; + + // A project must be open + pProj = m_pProjMgr->curProject(); + if (!pProj) + return; + + // Cannot update the file list of a temporary project + if (pProj->isTemporary()) { + KMessageBox::error(0, i18n("The Add/Remove Files dialogue is not " + "available for temporary projects.")); + return; + } + + // Display the files dialog + ProjectFilesDlg dlg((Project*)pProj, this); + if (dlg.exec() != QDialog::Accepted) + return; + + // Update the project's file list + if (pProj->storeFileList(&dlg)) + slotProjectFilesChanged(); +} + +/** + * Handles the "Project->Properties..." command. + * Opens the project's properties dialog, which allows the user to change + * some attributes of the current project. + * source files. + */ +void KScope::slotProjectProps() +{ + ProjectBase* pProj; + ProjectBase::Options opt; + + // A project must be open + pProj = m_pProjMgr->curProject(); + if (!pProj) + return; + + // No properties for a temporary project + if (pProj->isTemporary()) { + KMessageBox::error(0, i18n("The Project Properties dialogue is not " + "available for temporary projects.")); + return; + } + + // Create the properties dialog + NewProjectDlg dlg(false, this); + pProj->getOptions(opt); + dlg.setProperties(pProj->getName(), pProj->getPath(), opt); + + // Display the properties dialog + if (dlg.exec() != QDialog::Accepted) + return; + + // Set new properties + dlg.getOptions(opt); + pProj->setOptions(opt); + + // Reset the CscopeFrontend class and the builder object + initCscope(); + + // Set auto-completion parameters + SymbolCompletion::initAutoCompletion(opt.bACEnabled, opt.nACMinChars, + opt.nACDelay, opt.nACMaxEntries); + + // Set per-project command-line arguments for Ctags + CtagsFrontend::setExtraArgs(opt.sCtagsCmd); + + // Set the source root + m_pFileView->setRoot(pProj->getSourceRoot()); +} + +/** + * Handles the "Cscope->Open Cscope.out..." menu command. + * Prompts the user for a Cscope.out file, and, if successful, opens a new + * session for working with this file. + */ +void KScope::slotProjectCscopeOut() +{ + QString sFilePath; + + // Prompt for a Cscope.out file + sFilePath = KFileDialog::getOpenFileName(); + if (sFilePath.isEmpty()) + return; + + // Open a temporary project + openCscopeOut(sFilePath); +} + +/** + * Handles the "Cscope->References..." menu command. + * Prompts the user for a symbol name, and initiates a query to find all + * references to that symbol. + */ +void KScope::slotQueryReference() +{ + slotQuery(SymbolDlg::Reference, true); +} + +/** + * Handles the "Cscope->Definition..." menu command. + * Prompts the user for a symbol name, and initiates a query to find the + * global definition of that symbol. + */ +void KScope::slotQueryDefinition() +{ + slotQuery(SymbolDlg::Definition, true); +} + +/** + * Handles the "Cscope->Called Functions..." menu command. + * Prompts the user for a function name, and initiates a query to find all + * function calls from that function. + */ +void KScope::slotQueryCalled() +{ + slotQuery(SymbolDlg::Called, true); +} + +/** + * Handles the "Cscope->Calling Functions..." menu command. + * Prompts the user for a function name, and initiates a query to find all + * functions calling that function. + */ +void KScope::slotQueryCalling() +{ + slotQuery(SymbolDlg::Calling, true); +} + +/** + * Handles the "Cscope->Find Text..." menu command. + * Prompts the user for a string, and initiates a query to find all +occurances + * of that string. + */ +void KScope::slotQueryText() +{ + slotQuery(SymbolDlg::Text, true); +} + +/** + * Handles the "Cscope->Find EGrep Pattern..." menu command. + * Prompts the user for a regular expression, and initiates a query to find + * all strings matching that pattern. + */ +void KScope::slotQueryPattern() +{ + slotQuery(SymbolDlg::Pattern, true); +} + +/** + * Handles the "Cscope->Find File..." menu command. + * Prompts the user for a file name, and initiates a query to find all files + * having that name. + */ +void KScope::slotQueryFile() +{ + slotQuery(SymbolDlg::FileName, true); +} + +/** + * Handles the "Cscope->Find Including Files..." menu command. + * Prompts the user for a file name, and initiates a query to find all files + * having an '#include' directive to that file. + */ +void KScope::slotQueryIncluding() +{ + slotQuery(SymbolDlg::Including, true); +} + +/** + * Handles the "Cscope->Quick Definition" menu command. + * Initiates a query to find the global definition of the symbol currently + * selected or under the cursor. The user is prompted only if no symbol can + * be found. + */ +void KScope::slotQueryQuickDef() +{ + QString sSymbol; + QueryViewDlg* pDlg; + uint nType; + bool bCase; + + // Get the requested symbol and query type + nType = SymbolDlg::Definition; + if (!getSymbol(nType, sSymbol, bCase, false)) + return; + + // Create a modeless query view dialogue + pDlg = new QueryViewDlg(QueryViewDlg::DestroyOnSelect, this); + + // Display a line when it is selected in the dialogue + connect(pDlg, SIGNAL(lineRequested(const QString&, uint)), this, + SLOT(slotShowEditor(const QString&, uint))); + + // Start the query + pDlg->query(nType, sSymbol); +} + +/** + * Handles the "Cscope->Call Tree..." menu command. + * Displays a tree of functions calling the requested function. + */ +void KScope::slotCallTree() +{ + slotQuery(SymbolDlg::CallTree, true); +} + +/** + * Handles the "Cscope->Rebuild Database..." command. + * Rebuilds Cscope's database for the current project. + */ +void KScope::slotRebuildDB() +{ + ProjectBase* pProj; + + pProj = m_pProjMgr->curProject(); + if (!pProj) + return; + + if (!pProj->dbExists()) { + m_pProgressDlg = new ProgressDlg(i18n("KScope"), i18n("Please wait " + "while KScope builds the database"), this); + m_pProgressDlg->setAllowCancel(false); + m_pProgressDlg->setValue(0); + } + + m_pCscopeBuild->rebuild(); +} + +/** + * Handles the "Settings->Configure Shortcuts..." command. + * Displays the prferences dialog, which allows the user + * to change the shortcuts for KScope. + */ +void KScope::slotShortcuts() +{ + KKeyDialog::configure(actionCollection(), this); +} + +/** + * Handles the "Settings->Configure KScope..." command. + * Displays the prferences dialog, which allows the user to set different + * configuration parameters for KScope. + */ +void KScope::slotConfigure() +{ + PreferencesDlg dlg; + + // Apply the preferences if either the "Apply" or the "OK" buttons are + // clicked + connect(&dlg, SIGNAL(applyPref()), this, SLOT(slotApplyPref())); + + // Show the dialog + if (dlg.exec() == QDialog::Accepted) { + // Verify Cscope's installation + verifyCscope(); + } +} + +/** + * Refreshes the file list when files are added to or removed from a project, + * and rebuilds the Cscope database. + * This slot is attached to the fileListChanged() signal emitted by + * the ProjectManager object. + */ +void KScope::slotProjectFilesChanged() +{ + QStringList slArgs; + + // Refresh the file list + m_pFileList->setUpdatesEnabled(false); + m_pFileList->clear(); + m_pProjMgr->curProject()->loadFileList(m_pFileList); + m_pFileList->setUpdatesEnabled(true); + + // Rebuild the symbol database + if (isAutoRebuildEnabled()) + slotRebuildDB(); +} + +/** + * Adds a list of files to the current project. + * This slot is connected to the filesAdded() signal of the ProjectManager + * object. Once files are added to the project, they are also added to the + * file list, and the project's database is rebuilt. + * @param slFiles The list of file paths added to the project + */ +void KScope::slotFilesAdded(const QStringList& slFiles) +{ + QStringList::const_iterator itr; + + // Add the file paths to the project's file list + for (itr = slFiles.begin(); itr != slFiles.end(); ++itr) + m_pFileList->addItem(*itr); + + // Rebuild the database + if (isAutoRebuildEnabled()) + slotRebuildDB(); +} + +/** + * Promts the user for a symbol, an starts a new Cscope query. + * @param nType The numeric query type code + * @param bPrompt true to always prompt for a symbol, false to try to + * obtain the symbol automatically + */ +void KScope::slotQuery(uint nType, bool bPrompt) +{ + QString sSymbol; + CallTreeDlg* pCallTreeDlg; + bool bCase; + + // Get the requested symbol and query type + if (!getSymbol(nType, sSymbol, bCase, bPrompt)) + return; + + if (nType == SymbolDlg::CallTree) { + // Create and display a call tree dialogue + pCallTreeDlg = m_pCallTreeMgr->addDialog(); + pCallTreeDlg->setRoot(sSymbol); + pCallTreeDlg->show(); + } + else { + // Run the requested query + nType = SymbolDlg::getQueryType(nType); + m_pQueryWidget->initQuery(nType, sSymbol, bCase); + + // Ensure Query Window is visible + toggleQueryWindow(true); + } +} + +/** + * Opens a project. + * If another project is currently active, it is closed first. + * @param sDir The directory of the project to open. + */ +void KScope::openProject(const QString& sDir) +{ + QString sProjDir; + ProjectBase* pProj; + QStringList slQueryFiles; + QStringList slCallTreeFiles; + QStringList slArgs; + ProjectBase::Options opt; + + // Close the current project (may return false if the user clicks on the + // "Cancel" button while prompted to save a file) + if (!slotCloseProject()) + return; + + // Open the project in the project manager + sProjDir = QDir::cleanDirPath(sDir); + if (!m_pProjMgr->open(sProjDir)) + return; + + // Change main window title + pProj = m_pProjMgr->curProject(); + setCaption(pProj->getName()); + + // Set the root of the file tree + m_pFileView->setRoot(pProj->getSourceRoot()); + + // Initialise Cscope and create a builder object + initCscope(); + + // Set auto-completion parameters + pProj->getOptions(opt); + SymbolCompletion::initAutoCompletion(opt.bACEnabled, opt.nACMinChars, + opt.nACDelay, opt.nACMaxEntries); + + // Set per-project command-line arguments for Ctags + CtagsFrontend::setExtraArgs(opt.sCtagsCmd); + + // Create an initial query page + m_pQueryWidget->addQueryPage(); + + // Enable project-related actions + m_pActions->slotEnableProjectActions(true); + + // If this is a new project (i.e., no source files are yet included), + // display the project files dialogue + if (pProj->isEmpty()) { + slotProjectFiles(); + return; + } + + // Fill the file list with all files in the project. + m_pFileList->setUpdatesEnabled(false); + pProj->loadFileList(m_pFileList); + m_pFileList->setUpdatesEnabled(true); + + // Restore the last session + restoreSession(); + + // Rebuild the cross-reference database + if (isAutoRebuildEnabled()) { + // If Cscope installation was not yet verified, postpone the build + // process + if (m_bCscopeVerified) + slotRebuildDB(); + else + m_bRebuildDB = true; + } +} + +/** + * Opens a temporary project for a Cscope.out file. + * @param sFilePath The full path of the Cscope.out file + * @return true if successful, false otherwise + */ +bool KScope::openCscopeOut(const QString& sFilePath) +{ + ProjectBase* pProj; + + // Close the current project (may return false if the user clicks on the + // "Cancel" button while prompted to save a file) + if (!slotCloseProject()) + return false; + + // Open a temporary project for this cscope.out file + if (!m_pProjMgr->openCscopeOut(sFilePath)) + return false; + + // Change main window title + pProj = m_pProjMgr->curProject(); + setCaption(pProj->getName()); + + // Set the root folder in the file tree + m_pFileView->setRoot(pProj->getSourceRoot()); + + // Initialise Cscope and create a builder object + initCscope(); + + // Create an initial query page + m_pQueryWidget->addQueryPage(); + + // Enable project-related actions + m_pActions->slotEnableProjectActions(true); + + // Fill the file list with all files in the project. + m_pFileList->setUpdatesEnabled(false); + pProj->loadFileList(m_pFileList); + m_pFileList->setUpdatesEnabled(true); + + return true; +} + +/** + * Opens the most recently used project. + * This method is called when KScope starts, but only if the relevant + * configuration flag (Reload Last Project) is set. + */ +void KScope::openLastProject() +{ + const QStringList slProjects = Config().getRecentProjects(); + QString sPath; + + if (slProjects.empty()) + return; + + // Get the project's path + sPath = *slProjects.begin(); + + // Check if the path refers to a temporary project + if (!QFileInfo(sPath).isDir()) { + openCscopeOut(sPath); + return; + } + + openProject(sPath); +} + +/** + * Reopens all files which were open when the project was last closed. + * In order to reduce the time required by this operation, the GUI of all + * but the last editor part is not merged with that of the main window. + */ +void KScope::restoreSession() +{ + ProjectBase* pProj; + Project::Session sess; + FileLocation* pLoc; + EditorPage* pPage; + + // A session is available for persistent projects only + pProj = m_pProjMgr->curProject(); + if (!pProj || pProj->isTemporary()) + return; + + // Make sure all FileLocation objects are deleted + sess.fllOpenFiles.setAutoDelete(true); + sess.fllBookmarks.setAutoDelete(true); + + // Load the session + ((Project*)pProj)->loadSession(sess); + + // Do not update the GUI when loading the editor parts of the initially + // hidden windows + m_bUpdateGUI = false; + + for (pLoc = sess.fllOpenFiles.first(); pLoc != NULL; + pLoc = sess.fllOpenFiles.next()) { + if (QFile::exists(pLoc->m_sPath)) { + pPage = addEditor(pLoc->m_sPath); + pPage->setCursorPos(pLoc->m_nLine, pLoc->m_nCol); + } + } + + // Merge the GUI of the visible editor part + m_bUpdateGUI = true; + + // Set the active editor (or choose a default one) + if (m_pEditTabs->findEditorPage(sess.sLastFile, true) == NULL) + m_pEditTabs->findEditorPage(sess.fllOpenFiles.last()->m_sPath, true); + + // Reload bookmarks + m_pEditTabs->setBookmarks(sess.fllBookmarks); + + // Load previously stored queries and call trees + m_pQueryWidget->loadPages(pProj->getPath(), sess.slQueryFiles); + m_pCallTreeMgr->loadOpenDialogs(pProj->getPath(), sess.slCallTreeFiles); +} + +/** + * Shows or hides the query dock window. + * This function is only called internally, not as a result of a user's + * workspace action (e.g., clicking the "Show/Hide Query Window" toolbar + * button). Therefore it does not reflect the user's preference, which is + * kept through the m_bHideQueryOnSelection variable. + * @param bShow true to show the window, false to hide it + */ +void KScope::toggleQueryWindow(bool bShow) +{ + // Remember the user's preferences + if (bShow) + m_bHideQueryOnSelection = m_pQueryDock->isHidden(); + else + m_bHideQueryOnSelection = false; + + // Change the visibility state of the widget, if required + if (m_pQueryDock->isShown() != bShow) + m_pQueryDock->changeHideShowState(); + + // Synchronise with the menu command's state + m_pActions->slotQueryDockToggled(bShow); +} + +/** + * Parses the command line, after it was stripped of its KDE options. + * The command line may contain one of the following options: + * 1. A project file (named cscope.proj) + * 2. A Cscope cross-reference database + * 3. A list of source files + * @param pArgs Command line arguments + */ +void KScope::parseCmdLine(KCmdLineArgs* pArgs) +{ + QString sArg; + QFileInfo fi; + int i; + + // Loop over all arguments + for (i = 0; i < pArgs->count(); i++) { + // Verify the argument is a file or directory name + sArg = pArgs->arg(i); + fi.setFile(sArg); + if (!fi.exists()) + continue; + + // Handle the current argument + if (fi.isFile()) { + if (fi.fileName() == "cscope.proj") { + // Open a project file + openProject(fi.dirPath(true)); + return; + } else if (openCscopeOut(sArg)) { + // Opened the file as a cross-reference database + return; + } else { + // Assume this is a source file + slotShowEditor(sArg, 0); + } + } else if (fi.isDir()) { + // Treat the given path as a project directory + openProject(fi.absFilePath()); + return; + } + } +} + +/** + * Starts a shell script to ensure that Cscope is properly installed and to + * extract the supported command-line arguments. + */ +void KScope::verifyCscope() +{ + CscopeVerifier* pVer; + + statusBar()->message(i18n("Verifying Cscope installation...")); + + pVer = new CscopeVerifier(); + connect(pVer, SIGNAL(done(bool, uint)), this, + SLOT(slotCscopeVerified(bool, uint))); + + pVer->verify(); +} + +/** + * Initialises the CscopeFrontend class with the current project arguments, + * and creates an object used for rebuilding the symbol database. + */ +void KScope::initCscope() +{ + ProjectBase* pProj; + + // Delete the current object, if one exists + if (m_pCscopeBuild) + delete m_pCscopeBuild; + + // Initialise CscopeFrontend + pProj = m_pProjMgr->curProject(); + CscopeFrontend::init(pProj->getPath(), pProj->getArgs()); + + // Create a persistent Cscope process + m_pCscopeBuild = new CscopeFrontend(); + + // Show build progress information in the main status bar + connect(m_pCscopeBuild, SIGNAL(progress(int, int)), this, + SLOT(slotBuildProgress(int, int))); + connect(m_pCscopeBuild, SIGNAL(buildInvIndex()), this, + SLOT(slotBuildInvIndex())); + connect(m_pCscopeBuild, SIGNAL(finished(uint)), this, + SLOT(slotBuildFinished(uint))); + connect(m_pCscopeBuild, SIGNAL(aborted()), this, + SLOT(slotBuildAborted())); + + // Show errors in a modeless dialogue + connect(m_pCscopeBuild, SIGNAL(error(const QString&)), this, + SLOT(slotCscopeError(const QString&))); +} + +/** + * Closes the active project. + * Closing a project involves closing all of the editor windows (prompting + * the user for unsaved changes); terminating the Cscope process; and further + * clean-up of the project's data. + */ +bool KScope::slotCloseProject() +{ + ProjectBase* pProj; + Project::Session sess; + + // Do nothing if no project is open + pProj = m_pProjMgr->curProject(); + if (!pProj) + return true; + + // Make sure all FileLocation objects are deleted + sess.fllOpenFiles.setAutoDelete(true); + sess.fllBookmarks.setAutoDelete(true); + + // Close all open editor pages + if (m_pEditTabs->count() > 0) { + // Save session information for persistent projects + if (!pProj->isTemporary()) { + sess.sLastFile = m_pEditTabs->getCurrentPage()->getFilePath(); + m_pEditTabs->getOpenFiles(sess.fllOpenFiles); + m_pEditTabs->getBookmarks(sess.fllBookmarks); + } + + if (!m_pEditTabs->removeAllPages()) + return false; + } + + // Disable project-related actions + m_pActions->slotEnableProjectActions(false); + + // Destroy the make dialogue + if (m_pMakeDlg != NULL) { + // Save session information for persistent projects + if (!pProj->isTemporary()) { + sess.sMakeCmd = m_pMakeDlg->getCommand(); + sess.sMakeRoot = m_pMakeDlg->getDir(); + } + + delete m_pMakeDlg; + m_pMakeDlg = NULL; + } + + // Save session information for persistent projects + if (!pProj->isTemporary()) { + m_pQueryWidget->savePages(pProj->getPath(), sess.slQueryFiles); + m_pCallTreeMgr->saveOpenDialogs(pProj->getPath(), sess.slCallTreeFiles); + } + + // Close all query pages and call trees + m_pQueryWidget->slotCloseAll(); + m_pCallTreeMgr->closeAll(); + + // Store session information for persistent projects + if (!pProj->isTemporary()) + ((Project*)pProj)->storeSession(sess); + + // Close the project in the project manager, and terminate the Cscope + // process + m_pProjMgr->close(); + delete m_pCscopeBuild; + m_pCscopeBuild = NULL; + setCaption(QString::null); + + // Clear the contents of the file list + m_pFileView->clear(); + + // Reset queried symbols history + SymbolDlg::resetHistory(); + + // Remove any remaining status bar messages + statusBar()->message(""); + + return true; +} + +/** + * Handles the "Edit->Edit in External Editor" menu command. + * Invokes an external editor for the current file and line number. + */ +void KScope::slotExtEdit() +{ + QString sCmdLine; + KProcess proc; + + // Create the command line for the external editor + sCmdLine = Config().getExtEditor(); + sCmdLine.replace("%F", m_sCurFilePath); + sCmdLine.replace("%L", QString::number(m_nCurLine)); + + // Run the external editor + proc.setUseShell(true); + proc << sCmdLine; + proc.start(KProcess::DontCare); +} + +/** + * Handles the "Edit->Complete Symbol" menu command. + * Creates a list of possible completions for the symbol currently under the + * cursor. + */ +void KScope::slotCompleteSymbol() +{ + EditorPage* pPage; + + pPage = m_pEditTabs->getCurrentPage(); + if (pPage != NULL) + pPage->slotCompleteSymbol(); +} + +/** + * Handles the "Help->Show Welcome Message..." menu command. + * Displays the "Welcome" dialogue. + */ +void KScope::slotShowWelcome() +{ + WelcomeDlg dlg; + dlg.exec(); +} + +/** + * Handles the "Edit->Go To Tag" menu command. + * Sets the cursor to the edit box of the current tag list. + */ +void KScope::slotGotoTag() +{ + EditorPage* pPage; + + pPage = m_pEditTabs->getCurrentPage(); + if (pPage) + pPage->setTagListFocus(); +} + +/** + * Reports the results of the Cscope verification script. + * This slot is connected to the done() signal emitted by the CscopeVerifier + * object constructed in verifyCscope(). + */ +void KScope::slotCscopeVerified(bool bResult, uint nArgs) +{ + statusBar()->message(i18n("Verifying Cscope installation...Done"), 3000); + + // Mark the flag even if Cscope was not found, to avoid nagging the user + // (who may wish to use KScope even with Cscope disabled) + m_bCscopeVerified = true; + + // Prompt the user in case Cscope is not properly installed + if (!bResult) { + KMessageBox::error(0, i18n("Cscope may not be properly installed on " + "this system.\nPlease check the Cscope path specified in KScope's " + "configuration dialogue.")); + slotConfigure(); + return; + } + + // Set the discoverred supported command-line arguments + CscopeFrontend::setSupArgs(nArgs); + + // Build the database, if required + if (m_bRebuildDB) { + m_bRebuildDB = false; + slotRebuildDB(); + } +} + +/** + * Handles the "Project->Make..." menu command. + * Displays the make dialogue. + */ +void KScope::slotProjectMake() +{ + QString sCmd, sDir; + + // Create the make dialogue, if it does not exist + if (m_pMakeDlg == NULL) { + // Create the dialogue + m_pMakeDlg = new MakeDlg(); + + // Set make parameters for this project + m_pProjMgr->curProject()->getMakeParams(sCmd, sDir); + m_pMakeDlg->setCommand(sCmd); + m_pMakeDlg->setDir(sDir); + + // Show the relevant source location when an error link is clicked + connect(m_pMakeDlg, SIGNAL(fileRequested(const QString&, uint)), this, + SLOT(slotShowEditor(const QString&, uint))); + + // Show the dialogue + m_pMakeDlg->show(); + } + else if (m_pMakeDlg->isShown()) { + // The dialogue exists, and is visible, just raise it + m_pMakeDlg->raise(); + m_pMakeDlg->setActiveWindow(); + } + else { + // The dialogue exists but is closed, show it + m_pMakeDlg->show(); + } +} + +/** + * Handles the "Project->Remake..." menu command. + * Displays the make dialogue and runs the make command. + */ +void KScope::slotProjectRemake() +{ + // Make sure the make dialogue exists and is displayed + slotProjectMake(); + + // Run the make command + m_pMakeDlg->slotMake(); +} + +/** + * Handles the "Go->Global Bookmarks" menu command. + * Displays a dialogue with the set of all bookmarks currently set in this + * project. + */ +void KScope::slotShowBookmarks() +{ + BookmarksDlg dlg; + QString sPath; + uint nLine; + + // Load the bookmark list + m_pEditTabs->showBookmarks(dlg.getView()); + + // Show the dialogue + if (dlg.exec() != QDialog::Accepted) + return; + + // Go to the selected bookmark + dlg.getBookmark(sPath, nLine); + slotShowEditor(sPath, nLine); +} + +/** + * Prompts the user for a symbol to query. + * Shows a dialog with a line edit widget, where the user can enter a symbol + * on which to query Cscope. The meaning of the symbol depends on the type of + * query. + * @param nType The requested type of query (may be changed in the + * dialogue) + * @param sSymbol Holds the requested symbol, upon successful return + * @param bPrompt If false, the user is prompted only if a symbol cannot be + * determined automatically + * @return true if the user hs enetered a symbol, false otherwise + */ +bool KScope::getSymbol(uint& nType, QString& sSymbol, bool& bCase, + bool bPrompt) +{ + EditorPage* pPage; + QString sSuggested; + + // Set the currently selected text, if any + if ((pPage = m_pEditTabs->getCurrentPage()) != NULL) + sSuggested = pPage->getSuggestedText(); + + // Return if a symbol was found, and prompting is turned off + if (!sSuggested.isEmpty() && !bPrompt) { + sSymbol = sSuggested; + return true; + } + + // Show the symbol dialogue + sSymbol = SymbolDlg::promptSymbol(this, nType, sSuggested, bCase); + + // Cannot accept empty strings + if (sSymbol.isEmpty()) + return false; + + return true; +} + +/** + * Opens a file in a new editor tab. + * If an editor page already exists for the requested file, it is selected. + * Otherwise, a new page is created, and the requested file is loaded. + * @param sFilePath The path of the file to open + * @return A pointer to the found or newly created editor page + */ +EditorPage* KScope::addEditor(const QString& sFilePath) +{ + EditorPage* pPage; + QString sAbsFilePath; + ProjectBase* pProj; + + // If the file name is given using a relative path, we need to convert + // it to an absolute one + // TODO: Project needs a translatePath() method + pProj = m_pProjMgr->curProject(); + if (sFilePath[0] != '/' && pProj) { + sAbsFilePath = QDir::cleanDirPath(pProj->getSourceRoot() + "/" + + sFilePath); + } + else { + sAbsFilePath = QDir::cleanDirPath(sFilePath); + } + + // Do not open a new editor if one exists for this file + pPage = m_pEditTabs->findEditorPage(sAbsFilePath); + if (pPage != NULL) + return pPage; + + // Create a new page + pPage = createEditorPage(); + + // Open the requested file + pPage->open(sAbsFilePath); + + return pPage; +} + +/** + * Creates a new editor page, and adds it to the editors tab widget. + * @return A pointer to the new page + */ +EditorPage* KScope::createEditorPage() +{ + KTextEditor::Document* pDoc; + EditorPage* pPage; + QPopupMenu* pMenu; + ProjectBase* pProj; + + // Load a new document part + pDoc = m_pEditMgr->add(); + if (pDoc == NULL) + return NULL; + + // Create the new editor page + pMenu = (QPopupMenu*)factory()->container(Config().getEditorPopupName(), + this); + pPage = new EditorPage(pDoc, pMenu, m_pEditTabs); + m_pEditTabs->addEditorPage(pPage); + + // Show the file's path in the main title + connect(pPage, SIGNAL(fileOpened(EditorPage*, const QString&)), this, + SLOT(slotFileOpened(EditorPage*, const QString&))); + + // Show cursor position in the status bar + connect(pPage, SIGNAL(cursorPosChanged(uint, uint)), this, + SLOT(slotShowCursorPos(uint, uint))); + + // Rebuild the database after a file has changed + connect(pPage, SIGNAL(fileSaved(const QString&, bool)), this, + SLOT(slotFileSaved(const QString&, bool))); + + // Handle file drops + connect(pPage->getView(), SIGNAL(dropEventPass(QDropEvent*)), this, + SLOT(slotDropEvent(QDropEvent*))); + + // Apply per-project configuration + pProj = m_pProjMgr->curProject(); + if (pProj && pProj->getTabWidth() > 0) + pPage->setTabWidth(pProj->getTabWidth()); + + return pPage; +} + +/** + * @return true if database auto-rebuild is enabled for the current project, + * false otherwise + */ +inline bool KScope::isAutoRebuildEnabled() +{ + ProjectBase* pProj; + + pProj = m_pProjMgr->curProject(); + return (pProj && pProj->getAutoRebuildTime() >= 0); +} + +/** + * Deletes an editor page after it has been removed. + * The document object associated with the page is removed from the part + * manager, and the view object is removed from the GUI manager. + * This slot is connected to the editorRemoved() signal of the EditorTabs + * object. + * @param pPage The editor page to delete + */ +void KScope::slotDeleteEditor(EditorPage* pPage) +{ + guiFactory()->removeClient(pPage->getView()); + m_pEditMgr->remove(pPage->getDocument()); + delete pPage; +} + +/** + * Sets an editor part as active when its owner tab is chosen. + * Whenever a different editor tab is chosen, its editor part should become + * the active part. This means that this part's GUI is merged with the + * application's, and that it responds to actions. + * @param pOldPage The editor page that has ceased to be active + * @param pNewPage The newly chosen editor page + */ +void KScope::slotChangeEditor(EditorPage* pOldPage, EditorPage* pNewPage) +{ + KXMLGUIFactory* pFactory = guiFactory(); + + // Remove the current GUI + if (pOldPage) + pFactory->removeClient(pOldPage->getView()); + + // Set the new active part and create its GUI + if (m_bUpdateGUI && pNewPage) { + m_pEditMgr->setActivePart(pNewPage->getDocument()); + pFactory->addClient(pNewPage->getView()); + m_sCurFilePath = pNewPage->getFilePath(); + setCaption(m_pProjMgr->getProjName() + " - " + m_sCurFilePath); + } + + // Enable/disable file-related actions, if necessary + if (pOldPage && !pNewPage) + m_pActions->slotEnableFileActions(false); + else if (!pOldPage && pNewPage) + m_pActions->slotEnableFileActions(true); +} + +/** + * Opens an editor for the given file and sets the cursor to the beginning of + * the requested line. + * @param sFilePath The full path of the file to open for editing + * @param nLine The number of the line on which to position the + * cursor, or 0 to maintain the cursor in its current + * position (which does not affect the position history) + */ +void KScope::slotShowEditor(const QString& sFilePath, uint nLine) +{ + EditorPage* pPage; + + // Save current position in the position history + if (nLine != 0 && (pPage = m_pEditTabs->getCurrentPage())) { + m_pQueryWidget->addHistoryRecord(m_sCurFilePath, m_nCurLine, + pPage->getLineContents(m_nCurLine)); + } + + // Open the requested file (or select an already-open editor page) + pPage = addEditor(sFilePath); + if (pPage == NULL) + return; + + // Make sure the main window is visible + raise(); + setWindowState(windowState() & ~WindowMinimized | WindowActive); + + if (nLine != 0) { + // Set the cursor to the requested line + pPage->slotGotoLine(nLine); + + // Add the new position to the position history + m_pQueryWidget->addHistoryRecord(m_sCurFilePath, m_nCurLine, + pPage->getLineContents(m_nCurLine)); + } +} + +/** + * A wrapper around slotShowEditor, that enables auto-hiding of the query + * widget after a query result has been chosen. + * This slot is connected to the lineRequested() signal emitted by a QueryPage + * object. + * @param sFilePath The full path of the file to open for editing + * @param nLine The number of the line on which to position the cursor + */ +void KScope::slotQueryShowEditor(const QString& sFilePath, uint nLine) +{ + // Hide the query window, if it was hidden before a query was initiated + if (m_bHideQueryOnSelection) + toggleQueryWindow(false); + + // Open an editor at the requested line + slotShowEditor(sFilePath, nLine); +} + +/** + * Handles the "Go->Position History" menu command. + * Ensures that the query window is visible, and selects the active history + * page. + */ +void KScope::slotHistoryShow() +{ + toggleQueryWindow(true); + m_pQueryWidget->selectActiveHistory(); +} + +/** + * Handles the "File->New" menu command. + * Creates an editor page for a new unnamed file. + */ +void KScope::slotNewFile() +{ + EditorPage* pPage; + + // Create the new editor page + pPage = createEditorPage(); + + // Mark the page as containing a new file + pPage->setNewFile(); +} + +/** + * Handles the "File->Open" menu command. + * Prompts the user for a file name, and opens it in a new editor page. + */ +void KScope::slotOpenFile() +{ + ProjectBase* pProj; + QStringList slFiles; + QStringList::Iterator itr; + + // Prompt the user for the file(s) to open. + pProj = m_pProjMgr->curProject(); + slFiles = KFileDialog::getOpenFileNames(pProj ? pProj->getSourceRoot() : + QString::null); + + // Open all selected files. + for (itr = slFiles.begin(); itr != slFiles.end(); ++itr) { + if (!(*itr).isEmpty()) + slotShowEditor(*itr, 0); + } +} + +/** + * Handles the "File->Close" menu command. + * Closes the currently active editor page. + */ +void KScope::slotCloseEditor() +{ + m_pEditTabs->removeCurrentPage(); +} + +/** + * Handles the "Window->Close All" menu command. + * Closes all open editor pages. + */ +void KScope::slotCloseAllWindows() +{ + m_bUpdateGUI = false; + m_pEditTabs->removeAllPages(); + m_bUpdateGUI = true; +} + +/** + * Displays error messages from a Cscope process. + * This slot is connected to the progress() signal emitted by the any + * Cscope process. + * @param sMsg The error message + */ +void KScope::slotCscopeError(const QString& sMsg) +{ + m_pMsgDlg->addText(sMsg); +} + +/** + * Reports progress information from the Cscope process responsible for + * rebuilding the cross-reference database. + * This slot is connected to the progress() signal emitted by the builder + * process. + * Progress information is displayed in the status bar. + * @param nFiles The number of files scanned + * @param nTotal The total number of files in the project + */ +void KScope::slotBuildProgress(int nFiles, int nTotal) +{ + QString sMsg; + + // Use the progress dialogue, if it exists (first time builds) + if (m_pProgressDlg) { + m_pProgressDlg->setValue((nFiles * 100) / nTotal); + return; + } + + // Show progress information + sMsg = i18n("Rebuilding the cross reference database...") + " " + + QString::number((nFiles * 100) / nTotal) + "%"; + statusBar()->message(sMsg); +} + +/** + * Reports to the user that Cscope has started building the inverted index. + * This slot is connected to the buildInvIndex() signal emitted by the + * builder process. + */ +void KScope::slotBuildInvIndex() +{ + if (m_pProgressDlg) { + m_pProgressDlg->setLabel(i18n("Please wait while KScope builds the " + "inverted index")); + m_pProgressDlg->setIdle(); + return; + } + + statusBar()->message(i18n("Rebuilding inverted index...")); +} + +/** + * Informs the user the database rebuild process has finished. + * This slot is connected to the finished() signal emitted by the builder + * process. + */ +void KScope::slotBuildFinished(uint) +{ + // Delete the progress dialogue, if it exists (first time builds) + if (m_pProgressDlg) { + delete m_pProgressDlg; + m_pProgressDlg = NULL; + return; + } + + // Show a message in the status bar + statusBar()->message(i18n("Rebuilding the cross reference database..." + "Done!"), 3000); +} + +/** + * Called if the build process failed to complete. + * This slot is connected to the aborted() signal emitted by the builder + * process. + */ +void KScope::slotBuildAborted() +{ + // Delete the progress dialogue, if it exists (first time builds) + if (m_pProgressDlg) { + delete m_pProgressDlg; + m_pProgressDlg = NULL; + + // Display a failure message + KMessageBox::error(0, i18n("The database could not be built.\n" + "Cross-reference information will not be available for this " + "project.\n" + "Please ensure that the Cscope parameters were correctly " + "entered in the \"Settings\" dialogue.")); + return; + } + + // Show a message in the status bar + statusBar()->message(i18n("Rebuilding the cross reference database..." + "Failed"), 3000); +} + +/** + * Applies the selected user preferences once the "Apply" or "OK" buttons in + * the preferences dialog is clicked. + */ +void KScope::slotApplyPref() +{ + m_pQueryWidget->applyPrefs(); + m_pFileList->applyPrefs(); + m_pEditTabs->applyPrefs(); + m_pEditMgr->applyPrefs(); + + // Enable/disable the external editor menu item + m_pActions->enableExtEditor(Config().useExtEditor()); +} + +/** + * Displays the current cursor position, whenever it is moved by the user. + * This slot is connected to the cursorPosChanged() signal emitted by an + * EditorPage object. + * @param nLine The new line number + * @param nCol The new column number + */ +void KScope::slotShowCursorPos(uint nLine, uint nCol) +{ + KStatusBar* pStatus = statusBar(); + QString sText; + + /* Show the line and column numbers. */ + QTextOStream(&sText) << " Line: " << nLine << " Col: " << nCol << " "; + pStatus->changeItem(sText, 0); + + /* Store the current line. */ + m_nCurLine = nLine; +} + +/** + * Stores the path of a newly opened file. + * This slot is connected to the fileOpened() signal emitted by an + * EditorPage object. + * @param sFilePath The full path of the opened file + */ +void KScope::slotFileOpened(EditorPage*, const QString& sFilePath) +{ + m_sCurFilePath = sFilePath; + setCaption(m_pProjMgr->getProjName() + " - " + m_sCurFilePath); +} + +/** + * Sets a timer for rebuilding the database after a file has been saved. + * This slot is connected to the fileSaved() signal emitted by an EditorPage + * object. + * The time period before rebuilding is determined on a per-project basis. + * @param sPath The full path of the modified file that caused this event + * @param bIsNew true if this is a new file, false otherwise + */ +void KScope::slotFileSaved(const QString& sPath, bool bIsNew) +{ + ProjectBase* pProj; + int nTime; + + pProj = m_pProjMgr->curProject(); + if (!pProj) + return; + + // Prompt the user to add this file to the current project + if (bIsNew && !pProj->isTemporary()) { + if (KMessageBox::questionYesNo(0, + i18n("Whould you like to add this file to the active project?")) == + KMessageBox::Yes) { + + // Add the path to the 'cscope.files' file + if (!((Project*)pProj)->addFile(sPath)) { + KMessageBox::error(0, i18n("Failed to write the file list.")); + return; + } + + // Add the path to the file list widget + m_pFileList->addItem(sPath); + + // Rebuild immediately + slotRebuildDB(); + return; + } + } + + // Get the project's auto-rebuild time + nTime = pProj->getAutoRebuildTime(); + + // Do nothing if the time is set to -1 + if (nTime == -1) + return; + + // Check if the file is included in the project (external files should + // not trigger the timer) + if (!m_pFileList->findFile(sPath)) + return; + + // Rebuild immediately for a time set to 0 + if (nTime == 0) { + slotRebuildDB(); + return; + } + + // Reset the rebuild timer + m_timerRebuild.start(nTime * 1000, true); +} + +/** + * Handles file drops inside the editors tab widget. + * Opens all files dropped over the widget. + * @param pEvent Pointer to an object containing the list of dropped files + */ +void KScope::slotDropEvent(QDropEvent* pEvent) +{ + KURL::List list; + KURL::List::Iterator itr; + + // Create a list of file URLs + if (!KURLDrag::decode(pEvent, list)) + return; + + // Open all files in the list + for (itr = list.begin(); itr != list.end(); ++itr) + addEditor((*itr).path()); +} + +#include "kscope.moc" diff --git a/src/kscope.desktop b/src/kscope.desktop new file mode 100644 index 0000000..453a4fc --- /dev/null +++ b/src/kscope.desktop @@ -0,0 +1,12 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=KScope +Exec=kscope +Icon=kscope +Type=Application +Comment=Source-editing environment for large C projects +Comment[fr]=Editeur de codes sources pour de gros projets en C +GenericName=Source editing environment +GenericName[fr]=Editeur de code source +Categories=Qt;KDE;Development + diff --git a/src/kscope.h b/src/kscope.h new file mode 100644 index 0000000..f1b646e --- /dev/null +++ b/src/kscope.h @@ -0,0 +1,235 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef KSCOPE_H +#define KSCOPE_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qtimer.h> +#include <kcmdlineargs.h> +#include <kapp.h> +#include <kparts/dockmainwindow.h> +#include <kparts/part.h> + +class ProjectManager; +class EditorTabs; +class FileView; +class FileList; +class QueryWidget; +class EditorManager; +class CscopeFrontend; +class EditorPage; +class ProgressDlg; +class CscopeMsgDlg; +class MakeDlg; +class CallTreeManager; +class KScopeActions; + +/** + * Defines the main window of KScope. + * The main window has both UI and functional tasks. As a window, it is + * composed of three parts: + * 1. The editing area (EditorTabs - a tab widget with editor pages) + * 2. The project pane (FileList - listing the files in the project) + * 3. The query pane (QueryWidget - a tab widget with pages displaying query + * results in lists) + * The main window also maintains the main menu, the toolbar and the status- + * bar, and is responsible for handling all the actions connected to these + * bars. + * As the application's main class, it is responsible for managing projects + * (using a ProjectManager object) and for running instances of Cscope + * (through a CscopeFrontend object). + * @author Elad Lahav + */ +class KScope : public KParts::DockMainWindow +{ + Q_OBJECT + +public: + KScope(QWidget* pParent = 0, const char* szName = 0); + ~KScope(); + + void openProject(const QString&); + void openLastProject(); + bool openCscopeOut(const QString&); + void parseCmdLine(KCmdLineArgs *pArgs); + void verifyCscope(); + +public slots: + void slotClose(); + +protected: + virtual bool queryClose(); + +private: + /** A project manager used to load projects and read their properties. */ + ProjectManager* m_pProjMgr; + + /** The editors tabbed window. */ + EditorTabs* m_pEditTabs; + + /** The file selection widget (project file list and OS file system + tree.) */ + FileView* m_pFileView; + + /** Pointer to the file list part of the FileView widget. */ + FileList* m_pFileList; + + /** The query results tabbed window. */ + QueryWidget* m_pQueryWidget; + + /** A KDE editor part manager, responsible for creating KTextEditor + parts. */ + EditorManager* m_pEditMgr; + + /** A Cscope process for building the database. */ + CscopeFrontend* m_pCscopeBuild; + + /** A timer for rebuilding the database after a file has been saved. */ + QTimer m_timerRebuild; + + /** Whether the query window should be hidden after the user selects an + item. */ + bool m_bHideQueryOnSelection; + + /** The file view docking area. */ + KDockWidget* m_pFileViewDock; + + /** The query window docking area. */ + KDockWidget* m_pQueryDock; + + /** A persistent dialog used to display error messages from Cscope. */ + CscopeMsgDlg* m_pMsgDlg; + + /** The path of the file currently being edited. */ + QString m_sCurFilePath; + + /** The line number of the current cursor position. */ + int m_nCurLine; + + /** Creates and maintains call tree dialogues. */ + CallTreeManager* m_pCallTreeMgr; + + /** A progress dialogue that is displayed when building the database for + the first time. */ + ProgressDlg* m_pProgressDlg; + + /** A flag indicating whether the GUI of the embedded editor should be + merged with that of KScope's. Can be turned off to save time when + loading/closing a number of editor parts. */ + bool m_bUpdateGUI; + + /** Set to true after a shell script has verified that Cscope is installed + and correctly configured on this system. + No Cscope operations should be run if this flag is set to false. */ + bool m_bCscopeVerified; + + /** + * Used to postpone rebuilding of the database, until Cscope is ready. + */ + bool m_bRebuildDB; + + /** + * A widget for running make. + */ + MakeDlg* m_pMakeDlg; + + /** + * Manages menu and tool-bar commands. + */ + KScopeActions* m_pActions; + + void initMainWindow(); + void initCscope(); + bool getSymbol(uint&, QString&, bool&, bool bPrompt = true); + EditorPage* addEditor(const QString&s); + EditorPage* createEditorPage(); + inline bool isAutoRebuildEnabled(); + void restoreSession(); + void toggleQueryWindow(bool); + + friend class KScopeActions; + +private slots: + // Menu actions + void slotNewFile(); + void slotOpenFile(); + void slotCloseEditor(); + void slotCreateProject(); + void slotOpenProject(); + void slotProjectFiles(); + void slotProjectProps(); + void slotProjectCscopeOut(); + bool slotCloseProject(); + void slotQueryReference(); + void slotQueryDefinition(); + void slotQueryCalled(); + void slotQueryCalling(); + void slotQueryText(); + void slotQueryPattern(); + void slotQueryFile(); + void slotQueryIncluding(); + void slotQueryQuickDef(); + void slotCallTree(); + void slotRebuildDB(); + void slotHistoryShow(); + void slotShortcuts(); + void slotConfigure(); + void slotCloseAllWindows(); + void slotExtEdit(); + void slotCompleteSymbol(); + void slotShowWelcome(); + void slotGotoTag(); + void slotProjectMake(); + void slotProjectRemake(); + void slotShowBookmarks(); + + // Other slots + void slotProjectFilesChanged(); + void slotFilesAdded(const QStringList&); + void slotQuery(uint, bool); + void slotDeleteEditor(EditorPage*); + void slotChangeEditor(EditorPage*, EditorPage*); + void slotShowEditor(const QString&, uint); + void slotFileOpened(EditorPage*, const QString&); + void slotFileSaved(const QString&, bool); + void slotCscopeError(const QString&); + void slotBuildProgress(int, int); + void slotBuildInvIndex(); + void slotBuildFinished(uint); + void slotBuildAborted(); + void slotApplyPref(); + void slotShowCursorPos(uint, uint); + void slotQueryShowEditor(const QString&, uint); + void slotDropEvent(QDropEvent*); + void slotCscopeVerified(bool, uint); +}; + +#endif diff --git a/src/kscope.lsm b/src/kscope.lsm new file mode 100644 index 0000000..1062526 --- /dev/null +++ b/src/kscope.lsm @@ -0,0 +1,16 @@ +Begin4 +Title: KScope +Version: 1.4.0 +Entered-date: ? +Description: A source-editing environment for KDE bsaed on Cscope +Keywords: KDE Cscope Source Browser Editor IDE C +Author: elad_lahav@users.sourceforge.net (Elad Lahav) +Maintained-by: elad_lahav@users.sourceforge.net (Elad Lahav) +Home-page: http://kscope.sourceforge.net +Alternate-site: +Primary-site: http://download.sourceforge.net/kscope/ + ?M kscope-1.4.0.tar.gz + ? kscope-1.4.0.lsm +Platform: Linux/Unix. Requires KDE. +Copying-policy: BSD +End diff --git a/src/kscope_config b/src/kscope_config new file mode 100644 index 0000000..25fad0f --- /dev/null +++ b/src/kscope_config @@ -0,0 +1,165 @@ +# Configures KScope parameters + +# Checks that the given executable is indeed a Cscope-compatible application +# and determines which options it supports. +verifyCscope() +{ + CSCOPE_EXE=`basename $CSCOPE_PATH` + + if [ $DEBUG ] + then + echo -n Checking $CSCOPE_EXE version... + fi + + # Get the executable's version + CSCOPE_VER_MAJOR=`$CSCOPE_PATH -V 2>&1 | grep -i $CSCOPE_EXE | sed -e "s/.*version \([1-9][0-9]*\)\.\([0-9]\).*/\1/"` + CSCOPE_VER_MINOR=`$CSCOPE_PATH -V 2>&1 | grep -i $CSCOPE_EXE | sed -e "s/.*version \([1-9][0-9]*\)\.\([0-9]\).*/\2/"` + + if [ -n "$CSCOPE_VER_MAJOR" -a "$CSCOPE_VER_MINOR" ] + then + echo $CSCOPE_VER_MAJOR.$CSCOPE_VER_MINOR + + if [ $DEBUG ] + then + echo -n Cscope support for line mode verbose output... + fi + + # Check for verbose output + if [ "`$CSCOPE_PATH -h 2>&1 | grep "\-v"`" ] + then + CSCOPE_VERBOSE=Yes + else + CSCOPE_VERBOSE=No + fi + echo $CSCOPE_VERBOSE + + if [ $DEBUG ] + then + echo -n Cscope support slow path definitions... + fi + + # Check for slow-path definitions + if [ "`$CSCOPE_PATH -h 2>&1 | grep "\-D"`" ] + then + CSCOPE_SLOWPATH=Yes + else + CSCOPE_SLOWPATH=No + fi + echo $CSCOPE_SLOWPATH + else + echo ERROR + if [ $DEBUG ] + then + echo -e "\n *** ERROR *** The \"cscope\" executable does not appear to be a Cscope compatible programme" + fi + fi +} + +DEBUG=0 +CSCOPE_OPTIONS_ONLY= + +# Parse command-line parameters +# Supported options: +# -d: Debug mode +# -co: Check Cscope options only +for opt in $@ +do + case "$opt" in + "-d") DEBUG=1 + ;; + "-co") CSCOPE_OPTIONS_ONLY=1 + ;; + esac +done + +if [ $DEBUG ] +then + echo -n Looking for cscope... +fi + +if [ -z "$CSCOPE_PATH" ] +then + CSCOPE_PATH=`which cscope` +fi + +if [ -n "$CSCOPE_PATH" -a -x "$CSCOPE_PATH" ] +then + echo $CSCOPE_PATH + verifyCscope +else + echo ERROR + if [ $DEBUG ] + then + echo -e "\n *** ERROR *** No Cscope executable found" + fi +fi + +if [ -n "$CSCOPE_OPTIONS_ONLY" ] +then + exit +fi + +if [ $DEBUG ] +then + echo -n Looking for Ctags... +fi + +if [ -z "$CTAGS_PATH" ] +then + for CTAGS_NAME in exctags ctags-exuberant exuberant-ctags ctags + do + CTAGS_PATH=`which $CTAGS_NAME` + if [ -n "$CTAGS_PATH" -a -x "$CTAGS_PATH" ] + then + break + fi + done +fi + +if [ -n "$CTAGS_PATH" ] +then + echo $CTAGS_PATH + + # echo -n Checking for Exuberant-Ctags compatibility... + + CTAGS_EXUB=`$CTAGS_PATH --help | grep -c "\-\-excmd=number"` + if [ $CTAGS_EXUB -gt 0 ] + then + CTAGS_EXUB_PATH=$CTAGS_PATH + echo Yes + else + echo ERROR + # echo -e "\n *** ERROR *** The \"ctags\" executable does not appear to be compatible with Exuberant Ctags" + fi + +else + echo ERROR + # echo -e "\n *** ERROR *** No Ctags executable found" +fi + +# echo -n Looking for Dot... + +if [ -z "$DOT_PATH" ] +then + DOT_PATH=`which dot` +fi + +if [ -n "$DOT_PATH" -a -x "$DOT_PATH" ] +then + echo $DOT_PATH + + # echo -n Checking if dot handles the -Tplain option... + + echo "digraph G {Hello->World}" | $DOT_PATH -Tplain 2>&1 /dev/null + if [ $? -eq 0 ] + then + echo Yes + else + echo ERROR + # echo -e "\n *** ERROR *** The \"dot\" executable does not support -Tplain" + fi + +else + echo ERROR + # echo -e "\n *** ERROR *** No Dot executable found" +fi diff --git a/src/kscopeactions.cpp b/src/kscopeactions.cpp new file mode 100644 index 0000000..d5c64fc --- /dev/null +++ b/src/kscopeactions.cpp @@ -0,0 +1,533 @@ +/*************************************************************************** + * + * Copyright (C) 2007 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, m_pWindow list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, m_pWindow list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <klocale.h> +#include "kscope.h" +#include "kscopeactions.h" +#include "kscopeconfig.h" +#include "filelist.h" +#include "editortabs.h" +#include "querywidget.h" + +KScopeActions::KScopeActions(KScope* pWindow) : QObject(), + m_pWindow(pWindow), + m_pCollection(pWindow->actionCollection()) +{ +} + +KScopeActions::~KScopeActions() +{ +} + +/** + * Connects menu bar and toolbar commands with class members. These members + * handle the actions associated with each command. + */ +void KScopeActions::init() +{ + // File menu + KStdAction::openNew(m_pWindow, SLOT(slotNewFile()), m_pCollection); + KStdAction::open(m_pWindow, SLOT(slotOpenFile()), m_pCollection); + KStdAction::close(m_pWindow, SLOT(slotCloseEditor()), m_pCollection); + KStdAction::quit(m_pWindow, SLOT(slotClose()), m_pCollection); + + addAction(i18n("Go to File List"), + NULL, + "Ctrl+Shift+O", + m_pWindow->m_pFileList, + SLOT(slotSetFocus()), + "file_open_file_from_list", + SIGNAL(toggleProject(bool))); + + addAction(i18n("Save Al&l"), + "save_all", + "Ctrl+L", + m_pWindow->m_pEditTabs, + SLOT(slotSaveAll()), + "file_save_all", + NULL); + + // Edit menu + m_pExtEditAction = addAction(i18n("Edit in E&xternal Editor"), + NULL, + "Ctrl+E", + m_pWindow, + SLOT(slotExtEdit()), + "edit_external_editor", + SIGNAL(toggleFile(bool))); + + addAction(i18n("Go To Tag"), + NULL, + "Ctrl+Shift+T", + m_pWindow, + SLOT(slotGotoTag()), + "edit_goto_tag", + SIGNAL(toggleFile(bool))); + + addAction(i18n("Complete Symbol"), + NULL, + "Ctrl+Space", + m_pWindow, + SLOT(slotCompleteSymbol()), + "edit_comp_symbol", + SIGNAL(toggleFile(bool))); + + // Project menu + addAction(i18n("&New Project..."), + NULL, + NULL, + m_pWindow, + SLOT(slotCreateProject()), + "project_new", + NULL); + + addAction(i18n("&Open Project..."), + "project_open", + NULL, + m_pWindow, + SLOT(slotOpenProject()), + "project_open", + NULL); + + addAction(i18n("Open &Cscope.out..."), + NULL, + NULL, + m_pWindow, + SLOT(slotProjectCscopeOut()), + "project_cscope_out", + NULL); + + addAction(i18n("Add/Remove &Files..."), + NULL, + NULL, + m_pWindow, + SLOT(slotProjectFiles()), + "project_add_rem_files", + SIGNAL(toggleProject(bool))); + + addAction(i18n("&Properties..."), + NULL, + NULL, + m_pWindow, + SLOT(slotProjectProps()), + "project_properties", + SIGNAL(toggleProject(bool))); + + addAction(i18n("&Make Project"), + "make_kdevelop", + "Ctrl+M", + m_pWindow, + SLOT(slotProjectMake()), + "project_make", + SIGNAL(toggleProject(bool))); + + addAction(i18n("&Remake Project"), + "rebuild", + "Ctrl+Shift+M", + m_pWindow, + SLOT(slotProjectRemake()), + "project_remake", + SIGNAL(toggleProject(bool))); + + addAction(i18n("&Close Project"), + "fileclose", + NULL, + m_pWindow, + SLOT(slotCloseProject()), + "project_close", + SIGNAL(toggleProject(bool))); + + // Cscope menu + addAction(i18n("&References..."), + NULL, + "Ctrl+0", + m_pWindow, + SLOT(slotQueryReference()), + "cscope_references", + SIGNAL(toggleProject(bool))); + + addAction(i18n("&Definition..."), + NULL, + "Ctrl+1", + m_pWindow, + SLOT(slotQueryDefinition()), + "cscope_definition", + SIGNAL(toggleProject(bool))); + + addAction(i18n("&Called Functions..."), + NULL, + "Ctrl+2", + m_pWindow, + SLOT(slotQueryCalled()), + "cscope_called", + SIGNAL(toggleProject(bool))); + + addAction(i18n("C&alling Functions..."), + NULL, + "Ctrl+3", + m_pWindow, + SLOT(slotQueryCalling()), + "cscope_calling", + SIGNAL(toggleProject(bool))); + + addAction(i18n("Find &Text..."), + NULL, + "Ctrl+4", + m_pWindow, + SLOT(slotQueryText()), + "cscope_text", + SIGNAL(toggleProject(bool))); + + addAction(i18n("Find &EGrep Pattern..."), + NULL, + "Ctrl+5", + m_pWindow, + SLOT(slotQueryPattern()), + "cscope_pattern", + SIGNAL(toggleProject(bool))); + + addAction(i18n("Find &File..."), + NULL, + "Ctrl+7", + m_pWindow, + SLOT(slotQueryFile()), + "cscope_file", + SIGNAL(toggleProject(bool))); + + addAction(i18n("&Including Files..."), + NULL, + "Ctrl+8", + m_pWindow, + SLOT(slotQueryIncluding()), + "cscope_including", + SIGNAL(toggleProject(bool))); + + addAction(i18n("&Quick Definition"), + NULL, + "Ctrl+]", + m_pWindow, + SLOT(slotQueryQuickDef()), + "cscope_quick_def", + SIGNAL(toggleProject(bool))); + + addAction(i18n("Call &Graph..."), + NULL, + "Ctrl+\\", + m_pWindow, + SLOT(slotCallTree()), + "cscope_call_tree", + SIGNAL(toggleProject(bool))); + + addAction(i18n("Re&build database"), + "vcs_update", + NULL, + m_pWindow, + SLOT(slotRebuildDB()), + "cscope_rebuild", + SIGNAL(toggleProject(bool))); + + // Go menu + addAction(i18n("P&revious Result"), + "up", + "Alt+Up", + m_pWindow->m_pQueryWidget, + SLOT(slotPrevResult()), + "go_prev_result", + SIGNAL(toggleProject(bool))); + + addAction(i18n("N&ext Result"), + "down", + "Alt+Down", + m_pWindow->m_pQueryWidget, + SLOT(slotNextResult()), + "go_next_result", + SIGNAL(toggleProject(bool))); + + addAction(i18n("&Previous Position"), + "back", + "Alt+Left", + m_pWindow->m_pQueryWidget, + SLOT(slotHistoryPrev()), + "go_prev_pos", + NULL); + + addAction(i18n("&Next Position"), + "forward", + "Alt+Right", + m_pWindow->m_pQueryWidget, + SLOT(slotHistoryNext()), + "go_next_pos", + NULL); + + addAction(i18n("Position &History"), + "history", + "Ctrl+h", + m_pWindow, + SLOT(slotHistoryShow()), + "go_history", + NULL); + + addAction(i18n("Global &Bookmarks"), + "bookmark", + "Ctrl+Shift+G", + m_pWindow, + SLOT(slotShowBookmarks()), + "go_bookmarks", + NULL); + + // View menu + m_pToggleFileViewAction = addToggle(i18n("Toggle File List"), + "view_sidetree", + "Ctrl+/", + m_pWindow->m_pFileViewDock, + SLOT(changeHideShowState()), + "view_toggle_filelist_dock", + NULL); + + m_pToggleQueryWindowAction = addToggle(i18n("Toggle Query Window"), + "view_top_bottom", + "Ctrl+.", + m_pWindow->m_pQueryDock, + SLOT(changeHideShowState()), + "view_toggle_query_dock", + NULL); + + m_pToggleTagListAction = addToggle(i18n("Toggle Tag List"), + "view_detailed", + "Ctrl+'", + m_pWindow->m_pEditTabs, + SLOT(slotToggleTagList()), + "view_toggle_tag_list", + NULL); + + // Window menu + addAction(i18n("Close &All"), + "fileclose", + NULL, + m_pWindow, + SLOT(slotCloseAllWindows()), + "window_close_all", + NULL); + + addAction(i18n("Go &Left"), + "back", + "Alt+Shift+Left", + m_pWindow->m_pEditTabs, + SLOT(slotGoLeft()), + "window_go_left", + NULL); + + addAction(i18n("Go &Right"), + "forward", + "Alt+Shift+Right", + m_pWindow->m_pEditTabs, + SLOT(slotGoRight()), + "window_go_right", + NULL); + + // Settings menu + KStdAction::preferences(m_pWindow, SLOT(slotConfigure()), m_pCollection); + KStdAction::keyBindings(m_pWindow, SLOT(slotShortcuts()), m_pCollection); + + // Help menu + addAction(i18n("Show &Welcome Message..."), + NULL, + NULL, + m_pWindow, + SLOT(slotShowWelcome()), + "help_welcome", + NULL); + + // Query widget popup menu + addAction(i18n("&New"), + "filenew", + NULL, + m_pWindow->m_pQueryWidget, + SLOT(slotNewQueryPage()), + "query_new", + SIGNAL(toggleProject(bool))); + + addAction(i18n("&Refresh"), + "reload", + NULL, + m_pWindow->m_pQueryWidget, + SLOT(slotRefreshCurrent()), + "query_refresh", + SIGNAL(toggleProject(bool))); + + m_pLockAction = addToggle(i18n("&Lock/Unlock"), + "encrypted", + NULL, + m_pWindow->m_pQueryWidget, + SLOT(slotLockCurrent()), + "query_toggle_locked", + SIGNAL(toggleProject(bool))); + + addAction(i18n("&Close"), + "fileclose", + NULL, + m_pWindow->m_pQueryWidget, + SLOT(slotCloseCurrent()), + "query_close", + SIGNAL(toggleProject(bool))); + + m_pExtEditAction->setEnabled(Config().useExtEditor()); +} + +void KScopeActions::initLayoutActions() +{ + m_pToggleFileViewAction->setChecked(m_pWindow->m_pFileViewDock->isShown()); + m_pToggleQueryWindowAction->setChecked(m_pWindow->m_pQueryDock->isShown()); + m_pToggleTagListAction->setChecked(Config().getShowTagList()); +} + +/** + * Enables/disables the "Edit in External Editor" command. + * @param bEnable true to enable the command, false to disable it + */ +void KScopeActions::enableExtEditor(bool bEnable) +{ + m_pExtEditAction->setEnabled(bEnable); +} + +void KScopeActions::slotQueryDockToggled(bool bVisible) +{ + m_pToggleQueryWindowAction->setChecked(bVisible); +} + +/** + * Ensures the "Show/Hide Query Window" action is unchecked when the dock + * is closed through its close button. + * This slot is conncted to the headerCloseButtonClicked() signal of the + * query window dock widget. + */ +void KScopeActions::slotQueryDockClosed() +{ + m_pToggleQueryWindowAction->setChecked(false); +} + +/** + * Ensures the "Show/Hide File View" action is unchecked when the dock + * is closed through its close button. + * This slot is conncted to the headerCloseButtonClicked() signal of the + * file view dock widget. + */ +void KScopeActions::slotFileViewDockClosed() +{ + m_pToggleFileViewAction->setChecked(false); +} + +/** + * Enables/disables all actions related to open projects. + * This slot should be called whenever a project is opened or closed. + * @param bEnable true to enable actions, false to disable + */ +void KScopeActions::slotEnableProjectActions(bool bEnable) +{ + emit toggleProject(bEnable); +} + +/** + * Enables/disables all actions related to open files. + * This slot should be called the first file is opened, or when the last one + * is closed. + * @param bEnable true to enable actions, false to disable + */ +void KScopeActions::slotEnableFileActions(bool bEnable) +{ + emit toggleFile(bEnable); +} + +/** + * Creates a new action. + * @param sCaption The text to display in the menu item + * @param szIcon Optional icon associated with the action + * @param szShortcut Optional key-combination string + * @param pReceiver The widget to receive the action's signal + * @param szSlot The widget's slot that connect to the signal + * @param szName The XML entry corresponding to the action + * @param szSignal Optional signal to connect to the setEnabled() slot of + * the action + * @return The newly created action object + */ +KAction* KScopeActions::addAction(const QString& sCaption, const char* szIcon, + const char* szShortcut, QWidget* pReceiver, const char* szSlot, + const char* szName, const char* szSignal) +{ + KAction* pAction; + + // Create the action + pAction = new KAction(sCaption, + szIcon, + szShortcut == NULL ? KShortcut() : KShortcut(szShortcut), + pReceiver, + szSlot, + m_pCollection, + szName); + + // Add to the given action list, if any + if (szSignal) + connect(this, szSignal, pAction, SLOT(setEnabled(bool))); + + return pAction; +} + +/** + * Creates a new toggle action. + * @param sCaption The text to display in the menu item + * @param szIcon Optional icon associated with the action + * @param szShortcut Optional key-combination string + * @param pReceiver The widget to receive the action's signal + * @param szSlot The widget's slot that connect to the signal + * @param szName The XML entry corresponding to the action + * @param szSignal Optional signal to connect to the setEnabled() slot of + * the action + * @return The newly created action object + */ +KToggleAction* KScopeActions::addToggle(const QString& sCaption, + const char* szIcon, const char* szShortcut, QWidget* pReceiver, + const char* szSlot, const char* szName, const char* szSignal) +{ + KToggleAction* pAction; + + // Create the action + pAction = new KToggleAction(sCaption, + szIcon, + szShortcut == NULL ? KShortcut() : KShortcut(szShortcut), + pReceiver, + szSlot, + m_pCollection, + szName); + + // Add to the given action list, if any + if (szSignal) + connect(this, szSignal, pAction, SLOT(setEnabled(bool))); + + return pAction; +} + +#include "kscopeactions.moc" diff --git a/src/kscopeactions.h b/src/kscopeactions.h new file mode 100644 index 0000000..4836310 --- /dev/null +++ b/src/kscopeactions.h @@ -0,0 +1,98 @@ +/*************************************************************************** + * + * Copyright (C) 2007 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef KSCOPEACTIONS_H +#define KSCOPEACTIONS_H + +#include <kaction.h> + +class KScope; + +typedef QPtrList<KAction> ActionList; + +/** + * A helper class for managing KScope's menu commands. + * @author Elad Lahav + */ +class KScopeActions : public QObject +{ + Q_OBJECT + +public: + KScopeActions(KScope*); + ~KScopeActions(); + + void init(); + void initPopups(); + void initLayoutActions(); + void enableExtEditor(bool); + + KToggleAction* getLockAction() { return m_pLockAction; } + +public slots: + void slotQueryDockToggled(bool); + void slotQueryDockClosed(); + void slotFileViewDockClosed(); + void slotEnableProjectActions(bool); + void slotEnableFileActions(bool); + +signals: + void toggleProject(bool bEnable); + void toggleFile(bool bEnable); + +private: + KScope* m_pWindow; + KActionCollection* m_pCollection; + + /** A list of actions that require an active project. */ + ActionList m_lstProjActions; + + /** A list of actions that require an active file. */ + ActionList m_lstFileActions; + + /** A toggle menu item for locking/unlocking query pages. */ + KToggleAction* m_pLockAction; + + /** The "Edit in External Editor" menu command. */ + KAction* m_pExtEditAction; + + /** The "Show/Hide File View" menu command. */ + KToggleAction* m_pToggleFileViewAction; + + /** The "Show/Hide Query Window" menu command. */ + KToggleAction* m_pToggleQueryWindowAction; + + /** The "Show/Hide Tag List" menu command. */ + KToggleAction* m_pToggleTagListAction; + + KAction* addAction(const QString&, const char*, const char*, QWidget*, + const char*, const char*, const char*); + KToggleAction* addToggle(const QString&, const char*, const char*, + QWidget*, const char*, const char*, const char*); +}; + +#endif diff --git a/src/kscopeconfig.cpp b/src/kscopeconfig.cpp new file mode 100644 index 0000000..3cc5094 --- /dev/null +++ b/src/kscopeconfig.cpp @@ -0,0 +1,768 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <kconfig.h> +#include <kapplication.h> +#include <kglobalsettings.h> +#include "kscopeconfig.h" + +// NOTE: +// This configuration file entry controls whether the welcome dialogue is +// displayed. Normally it only needs to be shown once, but the entry's name +// can be changed across versions to force the display of new information. +#define SHOW_WELCOME_ENTRY "ShowWelcomeDlg" + +/** + * Stores the display name and the configuration file entry for a configurable + * GUI element. + * @author Elad Lahav + */ +struct ElementInfo +{ + /** The display name of the element. */ + const char* szName; + + /** The configuration file entry. */ + const char* szEntry; +}; + +/** + * A list of GUI elements for which the colour can be configured. + */ +const ElementInfo eiColors[] = { + { "File List (Foreground)", "FileListFore" }, + { "File List (Background)", "FileListBack" }, + { "Tag List (Foreground)", "TagListFore" }, + { "Tag List (Background)", "TagListBack" }, + { "Query Window (Foreground)", "QueryListFore" }, + { "Query Window (Background)", "QueryListBack" }, + { "Call Graph (Background)", "GraphBack" }, + { "Call Graph (Nodes)", "GraphNode" }, + { "Call Graph (Text)", "GraphText" }, + { "Call Graph (Multi-Call Nodes)", "GraphMultiCall" } +}; + +/** + * A list of GUI elements for which the font can be configured. + */ +const ElementInfo eiFonts[] = { + { "File List", "FileList" }, + { "Tag List", "TagList" }, + { "Query Page", "QueryList" }, + { "Call Graph", "Graph" } +}; + +#define COLOR_NAME(_i) eiColors[_i].szName +#define COLOR_ENTRY(_i) eiColors[_i].szEntry +#define FONT_NAME(_i) eiFonts[_i].szName +#define FONT_ENTRY(_i) eiFonts[_i].szEntry + +KScopeConfig::ConfParams KScopeConfig::s_cpDef = { + "/usr/bin/cscope", // Cscope path + "/usr/bin/ctags", // Ctags path + "/usr/bin/dot", // Dot path + true, // Show the tag list + SPLIT_SIZES(), // Tag list width + { + QColor(black), // File list foreground + QColor(white), // File list background + QColor(black), // Tag list foreground + QColor(white), // Tag list background + QColor(black), // Query page foreground + QColor(white), // Query page background + QColor("#c0c0c0"), // Call graph background + QColor("#c0ff80"), // Call graph nodes + QColor(black), // Call graph text + QColor("#ff8000") + }, + { + QFont(), // Font definitions are overriden in load() + QFont(), + QFont(), + QFont() + }, + NameAsc, // Ctags sort order + false, // Read-only mode + true, // Load last project + true, // Automatic tag highlighting + false, // Brief query captions + true, // Warn when file is modified on the disk + true, // Sort files when a project is loaded + "kate --line %L %F", // External editor example + Fast, // System profile + Embedded, // Editor context menu + "TB", // Call graph orientation + 10, // Maximum calls per graph node + 0 // Default graph view +}; + +/** + * Class constructor. + */ +KScopeConfig::KScopeConfig() : m_bFontsChanged(false) +{ +} + +/** + * Class destructor. + */ +KScopeConfig::~KScopeConfig() +{ +} + +/** + * Reads KScope's parameters from the standard configuration file. + */ +void KScopeConfig::load() +{ + uint i; + + KConfig* pConf = kapp->config(); + + // Need a working instance to get the system's default font (cannot be + // initialised statically) + s_cpDef.fonts[FileList] = KGlobalSettings::generalFont(); + s_cpDef.fonts[TagList] = KGlobalSettings::generalFont(); + s_cpDef.fonts[QueryWindow] = KGlobalSettings::generalFont(); + s_cpDef.fonts[Graph] = KGlobalSettings::generalFont(); + + // Read the paths to required executables + pConf->setGroup("Programs"); + m_cp.sCscopePath = pConf->readEntry("CScope"); + m_cp.sCtagsPath = pConf->readEntry("CTags"); + m_cp.sDotPath = pConf->readEntry("Dot"); + + // Read size and position parameters + pConf->setGroup("Geometry"); + m_cp.bShowTagList = pConf->readBoolEntry("ShowTagList", + s_cpDef.bShowTagList); + m_cp.siEditor = pConf->readIntListEntry("Editor"); + if (m_cp.siEditor.empty()) + m_cp.siEditor << 200 << 800; + + // Read the recent projects list + pConf->setGroup("Projects"); + m_slProjects = pConf->readListEntry("Recent"); + + // Read colour settings + pConf->setGroup("Colors"); + for (i = 0; i <= LAST_COLOR; i++) { + m_cp.clrs[i] = pConf->readColorEntry(COLOR_ENTRY(i), + &s_cpDef.clrs[i]); + } + + // Read font settings + pConf->setGroup("Fonts"); + for (i = 0; i <= LAST_FONT; i++) { + m_cp.fonts[i] = pConf->readFontEntry(FONT_ENTRY(i), + &s_cpDef.fonts[i]); + } + + // Other options + pConf->setGroup("Options"); + m_cp.ctagSortOrder = + (CtagSort)pConf->readUnsignedNumEntry("CtagSortOrder", + s_cpDef.ctagSortOrder); + m_cp.bReadOnlyMode = pConf->readBoolEntry("ReadOnlyMode", + s_cpDef.bReadOnlyMode); + m_cp.bLoadLastProj = pConf->readBoolEntry("LoadLastProj", + s_cpDef.bLoadLastProj); + m_cp.bAutoTagHl = pConf->readBoolEntry("AutoTagHl", + s_cpDef.bAutoTagHl); + m_cp.bBriefQueryCaptions = pConf->readBoolEntry("BriefQueryCaptions", + s_cpDef.bBriefQueryCaptions); + m_cp.bWarnModifiedOnDisk = pConf->readBoolEntry("WarnModifiedOnDisk", + s_cpDef.bWarnModifiedOnDisk); + m_cp.bAutoSortFiles = pConf->readBoolEntry("AutoSortFiles", + s_cpDef.bAutoSortFiles); + m_cp.sExtEditor = pConf->readEntry("ExternalEditor", s_cpDef.sExtEditor); + m_cp.profile = (SysProfile)pConf->readUnsignedNumEntry("SystemProfile", + s_cpDef.profile); + m_cp.popup = (EditorPopup)pConf->readUnsignedNumEntry("EditorPopup", + s_cpDef.popup); + m_cp.sGraphOrient = pConf->readEntry("GraphOrientation", + s_cpDef.sGraphOrient); + m_cp.nGraphMaxNodeDegree = pConf->readNumEntry("GraphMaxNodeDegree", + s_cpDef.nGraphMaxNodeDegree); + m_cp.nDefGraphView = pConf->readNumEntry("DefGraphView", + s_cpDef.nDefGraphView); +} + +/** + * Sets default values to he configuration parameters (except for those where + * a default value has no meaning, such as the recent projects list). + */ +void KScopeConfig::loadDefault() +{ + m_cp = s_cpDef; +} + +/** + * Loads the layout of the main window. + * @param pMainWindow Pointer to the main docking window + */ +void KScopeConfig::loadWorkspace(KDockMainWindow* pMainWindow) +{ + pMainWindow->readDockConfig(kapp->config(), "Workspace"); +} + +/** + * Writes KScope's parameters from the standard configuration file. + */ +void KScopeConfig::store() +{ + uint i; + + KConfig* pConf = kapp->config(); + + // Write the paths to required executables + pConf->setGroup("Programs"); + pConf->writeEntry("CScope", m_cp.sCscopePath); + pConf->writeEntry("CTags", m_cp.sCtagsPath); + pConf->writeEntry("Dot", m_cp.sDotPath); + + // Write size and position parameters + pConf->setGroup("Geometry"); + pConf->writeEntry("ShowTagList", m_cp.bShowTagList); + pConf->writeEntry("Editor", m_cp.siEditor); + + // Write the recent projects list + pConf->setGroup("Projects"); + pConf->writeEntry("Recent", m_slProjects); + + // Write colour settings + pConf->setGroup("Colors"); + for (i = 0; i <= LAST_COLOR; i++) + pConf->writeEntry(COLOR_ENTRY(i), m_cp.clrs[i]); + + // Write font settings + if (m_bFontsChanged) { + pConf->setGroup("Fonts"); + for (i = 0; i <= LAST_FONT; i++) + pConf->writeEntry(FONT_ENTRY(i), m_cp.fonts[i]); + + m_bFontsChanged = false; + } + + // Other options + pConf->setGroup("Options"); + pConf->writeEntry("CtagSortOrder", (uint)m_cp.ctagSortOrder); + pConf->writeEntry("ReadOnlyMode", m_cp.bReadOnlyMode); + pConf->writeEntry("LoadLastProj", m_cp.bLoadLastProj); + pConf->writeEntry("AutoTagHl", m_cp.bAutoTagHl); + pConf->writeEntry("BriefQueryCaptions", m_cp.bBriefQueryCaptions); + pConf->writeEntry("WarnModifiedOnDisk", m_cp.bWarnModifiedOnDisk); + pConf->writeEntry("AutoSortFiles", m_cp.bAutoSortFiles); + pConf->writeEntry("ExternalEditor", m_cp.sExtEditor); + pConf->writeEntry("SystemProfile", (uint)m_cp.profile); + pConf->writeEntry("EditorPopup", (uint)m_cp.popup); + pConf->writeEntry("GraphOrientation", m_cp.sGraphOrient); + pConf->writeEntry("GraphMaxNodeDegree", m_cp.nGraphMaxNodeDegree); + pConf->writeEntry("DefGraphView", m_cp.nDefGraphView); + + // Do not report it's the first time on the next run + pConf->setGroup("General"); + pConf->writeEntry("FirstTime", false); + pConf->writeEntry(SHOW_WELCOME_ENTRY, false); +} + +/** + * Stores the layout of the main window. + * @param pMainWindow Pointer to the main docking window + */ +void KScopeConfig::storeWorkspace(KDockMainWindow* pMainWindow) +{ + pMainWindow->writeDockConfig(kapp->config(), "Workspace"); +} + +/** + * Determines if this is the first time KScope was launched by the current + * user. + * @return true if this is the first time, false otherwise + */ +bool KScopeConfig::isFirstTime() +{ + KConfig* pConf = kapp->config(); + + pConf->setGroup("General"); + return pConf->readBoolEntry("FirstTime", true); +} + +/** + * Determines if the welcome dialogue should be displayed. + * Note that while the dialogue is displayed on the first invocation of KScope, + * it may be required on other occasions (e.g., to display important information + * on a per-version basis) and thus it is separated from isFirstTime(). + * @return true if the dialogue should be shown, false otherwise + */ +bool KScopeConfig::showWelcomeDlg() +{ + KConfig* pConf = kapp->config(); + + pConf->setGroup("General"); + return pConf->readBoolEntry(SHOW_WELCOME_ENTRY, true); +} + +/** + * @return The full path of the Cscope executable + */ +const QString& KScopeConfig::getCscopePath() const +{ + return m_cp.sCscopePath; +} + +/** + * @param sPath The full path of the Cscope executable + */ +void KScopeConfig::setCscopePath(const QString& sPath) +{ + m_cp.sCscopePath = sPath; +} + +/** + * @return The full path of the Ctags executable + */ +const QString& KScopeConfig::getCtagsPath() const +{ + return m_cp.sCtagsPath; +} + +/** + * @param sPath The full path of the Ctags executable + */ +void KScopeConfig::setCtagsPath(const QString& sPath) +{ + m_cp.sCtagsPath = sPath; +} + +/** + * @return The full path of the Dot executable + */ +const QString& KScopeConfig::getDotPath() const +{ + return m_cp.sDotPath; +} + +/** + * @param sPath The full path of the Dot executable + */ +void KScopeConfig::setDotPath(const QString& sPath) +{ + m_cp.sDotPath = sPath; +} + +/** + * @return A sorted list of recently used project paths. + */ +const QStringList& KScopeConfig::getRecentProjects() const +{ + return m_slProjects; +} + +/** + * Adds the given project path to the beginning of the recently used projects + * list. + * @param sProjPath The path of the project to add + */ +void KScopeConfig::addRecentProject(const QString& sProjPath) +{ + QStringList::Iterator itr; + + itr = m_slProjects.find(sProjPath); + if (itr != m_slProjects.end()) + m_slProjects.remove(itr); + + m_slProjects.prepend(sProjPath); +} + +/** + * Removes the given project path from recently used projects list. + * @param sProjPath The path of the project to remove + */ +void KScopeConfig::removeRecentProject(const QString& sProjPath) +{ + m_slProjects.remove(sProjPath); +} + +/** + * @return true if the tag list should be visible, false otherwise + */ +bool KScopeConfig::getShowTagList() const +{ + return m_cp.bShowTagList; +} + +/** + * @param bShowTagList true to make the tag list visible, false otherwise + */ +void KScopeConfig::setShowTagList(bool bShowTagList) +{ + m_cp.bShowTagList = bShowTagList; +} + +/** + * @return A list containing the widths of the Ctags list part and the + * editor part in an editor page. + */ +const SPLIT_SIZES& KScopeConfig::getEditorSizes() const +{ + return m_cp.siEditor; +} + +/** + * @param siEditor A list containing the widths of the Ctags list part + * and the editor part in an editor page. + */ +void KScopeConfig::setEditorSizes(const SPLIT_SIZES& siEditor) +{ + m_cp.siEditor = siEditor; +} + +/** + * Finds a colour to use for a GUI element. + * @param ce Identifies the GUI element + * @return A reference to the colour object to use + */ +const QColor& KScopeConfig::getColor(ColorElement ce) const +{ + return m_cp.clrs[ce]; +} + +/** + * Returns the display name of a GUI element whose colour can be configured. + * @param ce The GUI element + * @return A name used in the colour configuration page + */ +QString KScopeConfig::getColorName(ColorElement ce) const +{ + return COLOR_NAME(ce); +} + +/** + * Sets a new colour to a GUI element. + * @param ce Identifies the GUI element + * @param clr The colour to use + */ +void KScopeConfig::setColor(ColorElement ce, const QColor& clr) +{ + m_cp.clrs[ce] = clr; +} + +/** + * Finds a font to use for a GUI element. + * @param fe Identifies the GUI element + * @return A reference to the font object to use + */ +const QFont& KScopeConfig::getFont(FontElement fe) const +{ + return m_cp.fonts[fe]; +} + +/** + * Returns the display name of a GUI element whose font can be configured. + * @param ce The GUI element + * @return A name used in the font configuration page + */ +QString KScopeConfig::getFontName(FontElement ce) const +{ + return FONT_NAME(ce); +} + +/** + * Sets a new font to a GUI element. + * @param fe Identifies the GUI element + * @param font The font to use + */ +void KScopeConfig::setFont(FontElement fe, const QFont& font) +{ + m_bFontsChanged = true; + m_cp.fonts[fe] = font; +} + +/** + * @return The column and direction by which the tags should be sorted + */ +KScopeConfig::CtagSort KScopeConfig::getCtagSortOrder() +{ + return m_cp.ctagSortOrder; +} + +/** + * @param ctagSortOrder The column and direction by which the tags should + * be sorted + */ +void KScopeConfig::setCtagSortOrder(CtagSort ctagSortOrder) +{ + m_cp.ctagSortOrder = ctagSortOrder; +} + +/** + * @return true to work in Read-Only mode, false otherwise + */ +bool KScopeConfig::getReadOnlyMode() +{ + return m_cp.bReadOnlyMode; +} + +/** + * @param bReadOnlyMode true to work in Read-Only mode, false otherwise + */ +void KScopeConfig::setReadOnlyMode(bool bReadOnlyMode) +{ + m_cp.bReadOnlyMode = bReadOnlyMode; +} + +/** + * @return true to load the last project on start-up, false otherwise + */ +bool KScopeConfig::getLoadLastProj() +{ + return m_cp.bLoadLastProj; +} + +/** + * @param bLoadLastProj true to load the last project on start-up, false + * otherwise + */ +void KScopeConfig::setLoadLastProj(bool bLoadLastProj) +{ + m_cp.bLoadLastProj = bLoadLastProj; +} + +/** + * @return true to enable tag highlighting based on cursor position, false + * to disable this feature + */ +bool KScopeConfig::getAutoTagHl() +{ + return m_cp.bAutoTagHl; +} + +/** + * @param bAutoTagHl true to enable tag highlighting based on cursor + * position, false to disable this feature + */ +void KScopeConfig::setAutoTagHl(bool bAutoTagHl) +{ + m_cp.bAutoTagHl = bAutoTagHl; +} + +/** + * @return true to use the short version of the query captions, false to use + * the long version + */ +bool KScopeConfig::getUseBriefQueryCaptions() +{ + return m_cp.bBriefQueryCaptions; +} + +/** + * @param bBrief true to use the short version of the query captions, false + * to use the long version + */ +void KScopeConfig::setUseBriefQueryCaptions(bool bBrief) +{ + m_cp.bBriefQueryCaptions = bBrief; +} + +/** + * @return true to warn user when file is modified on disk, false + * otherwise + */ +bool KScopeConfig::getWarnModifiedOnDisk() +{ + return m_cp.bWarnModifiedOnDisk; +} + +/** + * @param bWarn true to warn user when file is modified on disk, + * false otherwise + */ +void KScopeConfig::setWarnModifiedOnDisk(bool bWarn) +{ + m_cp.bWarnModifiedOnDisk = bWarn; +} + +/** + * @return true to sort files when a project is loaded, false otherwise + */ +bool KScopeConfig::getAutoSortFiles() +{ + return m_cp.bAutoSortFiles; +} + +/** + * @param bSort true to sort files when a project is loaded, false + * otherwise + */ +void KScopeConfig::setAutoSortFiles(bool bSort) +{ + m_cp.bAutoSortFiles = bSort; +} + +/** + * @return A command line for launching an external editor + */ +const QString& KScopeConfig::getExtEditor() +{ + return m_cp.sExtEditor; +} + +/** + * @param sExtEditor A command line for launching an external editor + */ +void KScopeConfig::setExtEditor(const QString& sExtEditor) +{ + m_cp.sExtEditor = sExtEditor; +} + +/** + * Determines if an external editor should be used. + * An external editor is used if KScope is in Read-Only mode, and a command- + * line for the editor was specified. + * @return true to use an external editor, false otherwise + */ +bool KScopeConfig::useExtEditor() +{ + return !m_cp.sExtEditor.isEmpty(); +} + +/** + * @return The chosen profile for this system (@see SysProfile) + */ +KScopeConfig::SysProfile KScopeConfig::getSysProfile() const +{ + return m_cp.profile; +} + +/** + * @param profile The system profile to use (@see SysProfile) + */ +void KScopeConfig::setSysProfile(KScopeConfig::SysProfile profile) +{ + m_cp.profile = profile; +} + +/** + * @return The chosen popup menu type for the embedded editor (@see + * EditorPopup) + */ +KScopeConfig::EditorPopup KScopeConfig::getEditorPopup() const +{ + return m_cp.popup; +} + +/** + * @return The name of the popup menu to use in the embedded editor + */ +QString KScopeConfig::getEditorPopupName() const +{ + switch (m_cp.popup) { + case Embedded: + return "ktexteditor_popup"; + + case KScopeOnly: + return "kscope_popup"; + } + + // Will not happen, but the compiler complains if no return statement is + // given here + return ""; +} + +/** + * @param popup The popup menu to use for the embedded editor (@see + * EditorPopup) + */ +void KScopeConfig::setEditorPopup(KScopeConfig::EditorPopup popup) +{ + m_cp.popup = popup; +} + +/** + * @return The default orientation for call graphs + */ +QString KScopeConfig::getGraphOrientation() const +{ + return m_cp.sGraphOrient; +} + +/** + * @param sOrient The default orientation for call graphs + */ +void KScopeConfig::setGraphOrientation(const QString& sOrient) +{ + m_cp.sGraphOrient = sOrient; +} + +/** + * @return The maximal number of calls per graph node + */ +int KScopeConfig::getGraphMaxNodeDegree() const +{ + return m_cp.nGraphMaxNodeDegree; +} + +/** + * @param nMaxNodeDegree The maximal number of calls per graph node + */ +void KScopeConfig::setGraphMaxNodeDegree(int nMaxNodeDegree) +{ + m_cp.nGraphMaxNodeDegree = nMaxNodeDegree; +} + +/** + * @return The default view in the call graph dialogue + */ +int KScopeConfig::getDefGraphView() const +{ + return m_cp.nDefGraphView; +} + +/** + * @param nDefGraphView The default view in the call graph dialogue + */ +void KScopeConfig::setDefGraphView(int nDefGraphView) +{ + m_cp.nDefGraphView = nDefGraphView; +} + +/** + * Returns a reference to a global configuration object. + * The static object defined is this function should be the only KSCopeConfig + * object in this programme. Any code that wishes to get or set global + * configuration parameters, should call Config(), instead of defining its + * own object. + * @return Reference to a statically allocated configuration object + */ +KScopeConfig& Config() +{ + static KScopeConfig conf; + return conf; +} + +#include "kscopeconfig.moc" diff --git a/src/kscopeconfig.h b/src/kscopeconfig.h new file mode 100644 index 0000000..8a047cf --- /dev/null +++ b/src/kscopeconfig.h @@ -0,0 +1,218 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef KSCOPECONFIG_H +#define KSCOPECONFIG_H + +#include <qobject.h> +#include <qstringlist.h> +#include <qcolor.h> +#include <kdockwidget.h> + +typedef QValueList<int> SPLIT_SIZES; + +/** + * Loads and stores global configuration parameters. + * @author Elad Lahav + */ + +class KScopeConfig : public QObject +{ + Q_OBJECT + +public: + KScopeConfig(); + ~KScopeConfig(); + + /** GUI elements whose colours can be set. */ + enum ColorElement { FileListFore = 0, FileListBack, TagListFore, + TagListBack, QueryWindowFore, QueryWindowBack, GraphBack, + GraphNode, GraphText, GraphMultiCall, LAST_COLOR = GraphMultiCall }; + + /** GUI elements whose fonts can be set. */ + enum FontElement { FileList = 0, TagList, QueryWindow, Graph, + LAST_FONT = Graph }; + + /** Sort order values for the tags list. */ + enum CtagSort { NameAsc = 0, NameDes, LineAsc, LineDes, TypeAsc, + TypeDes }; + + /** Types of systems that determine certain aspects in KScope's + behaviour. + For fast systems, certain time-consuming operations, such as + rebuilding the database, may be performed automatically. Such + behaviour, however, is not desired on slow systems, in which the user + should handle such operations manually. */ + enum SysProfile { Fast, Slow }; + + /** The different options for a popup menu to be installed in the editor + parts. */ + enum EditorPopup { Embedded, KScopeOnly }; + + void load(); + void loadDefault(); + void loadWorkspace(KDockMainWindow*); + void store(); + void storeWorkspace(KDockMainWindow*); + bool isFirstTime(); + bool showWelcomeDlg(); + + const QString& getCscopePath() const; + void setCscopePath(const QString&); + const QString& getCtagsPath() const; + void setCtagsPath(const QString&); + const QString& getDotPath() const; + void setDotPath(const QString&); + const QStringList& getRecentProjects() const; + void addRecentProject(const QString&); + void removeRecentProject(const QString&); + bool getShowTagList() const; + void setShowTagList(bool); + const SPLIT_SIZES& getEditorSizes() const; + void setEditorSizes(const SPLIT_SIZES&); + const QColor& getColor(ColorElement) const; + QString getColorName(ColorElement) const; + void setColor(ColorElement, const QColor&); + const QFont& getFont(FontElement) const; + QString getFontName(FontElement) const; + void setFont(FontElement, const QFont&); + CtagSort getCtagSortOrder(); + void setCtagSortOrder(CtagSort); + bool getReadOnlyMode(); + void setReadOnlyMode(bool); + bool getLoadLastProj(); + void setLoadLastProj(bool); + bool getAutoTagHl(); + void setAutoTagHl(bool); + bool getUseBriefQueryCaptions(); + void setUseBriefQueryCaptions(bool); + bool getWarnModifiedOnDisk(); + void setWarnModifiedOnDisk(bool); + bool getAutoSortFiles(); + void setAutoSortFiles(bool); + const QString& getExtEditor(); + void setExtEditor(const QString&); + bool useExtEditor(); + SysProfile getSysProfile() const; + void setSysProfile(SysProfile); + EditorPopup getEditorPopup() const; + QString getEditorPopupName() const; + void setEditorPopup(EditorPopup); + QString getGraphOrientation() const; + void setGraphOrientation(const QString&); + int getGraphMaxNodeDegree() const; + void setGraphMaxNodeDegree(int); + int getDefGraphView() const; + void setDefGraphView(int); + +private: + /** A list of previously loaded projects. */ + QStringList m_slProjects; + + /** Defines the list of all configurable parameters in KScope. + The use of a structure helps define default values (@see s_cpDef) */ + struct ConfParams { + /** The full path of the Cscope executable. */ + QString sCscopePath; + + /** The full path of the Ctags executable. */ + QString sCtagsPath; + + /** The full path of the Dot executable. */ + QString sDotPath; + + /** Whether the tag list should be visible. */ + bool bShowTagList; + + /** The widths of the tag list and editor panes inside an editor + page. */ + SPLIT_SIZES siEditor; + + /** Colours for GUI elements. */ + QColor clrs[LAST_COLOR + 1]; + + /** Fonts for GUI elements. */ + QFont fonts[LAST_FONT + 1]; + + /** Sort order of the tag lists. */ + CtagSort ctagSortOrder; + + /** Whether KScope should operate in code read-only mode. */ + bool bReadOnlyMode; + + /** Whether the last open project should be reloaded on start-up. */ + bool bLoadLastProj; + + /** Whether tags should be highlighted based on the current cursor + position. */ + bool bAutoTagHl; + + /** Whether query page captions should use mnemonics for query types, + instead of the full description. */ + bool bBriefQueryCaptions; + + /** Whether the warning should be displayed when file is modified on + disk by external process. */ + bool bWarnModifiedOnDisk; + + /** Should files be sorted automatically when a project is loaded. */ + bool bAutoSortFiles; + + /** A command line pattern for an external editor (in read-only + mode.)*/ + QString sExtEditor; + + /** How KScope should treat time-consuming operations. */ + SysProfile profile; + + /** The type of popup menu to use in the embedded editor. */ + EditorPopup popup; + + /** The default orientation of call graphs. */ + QString sGraphOrient; + + /** Maximal number of called/calling functions per call graph node. */ + int nGraphMaxNodeDegree; + + /** Default view for the call graph dialogue. */ + int nDefGraphView; + }; + + /** The current configuration parameters */ + ConfParams m_cp; + + /** Holds default values for the configuration parameters */ + static ConfParams s_cpDef; + + /** Write font preferences only if modified by the user (keep default + setting otherwise) */ + bool m_bFontsChanged; +}; + +extern KScopeConfig& Config(); + +#endif diff --git a/src/kscopepixmaps.cpp b/src/kscopepixmaps.cpp new file mode 100644 index 0000000..495d756 --- /dev/null +++ b/src/kscopepixmaps.cpp @@ -0,0 +1,376 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <kglobal.h> +#include <kiconloader.h> +#include "kscopepixmaps.h" + +static const char* XPM_FUNC[] = { + "12 12 2 1", + ". c #000000", + "# c #58a8ff", + "............", + ".##########.", + ".###....###.", + ".###.######.", + ".###.######.", + ".###.######.", + ".###....###.", + ".###.######.", + ".###.######.", + ".###.######.", + ".##########.", + "............" +}; + +static const char* XPM_VAR[] = { + "12 12 3 1", + ". c #000000", + "a c #c00000", + "# c #ff0000", + "............", + ".##########.", + ".##########.", + ".##.####.##.", + ".##.####.##.", + ".##.a##a.##.", + ".##a.##.a##.", + ".###.aa.###.", + ".###a..a###.", + ".####..####.", + ".##########.", + "............" +}; + +static const char* XPM_STRUCT[] = { + "12 12 2 1", + ". c #000000", + "# c #ffff00", + "............", + ".##########.", + ".####...###.", + ".###.###.##.", + ".###.######.", + ".####.#####.", + ".#####.####.", + ".######.###.", + ".##.###.###.", + ".###...####.", + ".##########.", + "............" +}; + +static const char* XPM_MACRO[] = { + "12 12 2 1", + ". c #000000", + "# c #00c000", + "............", + ".##########.", + ".##.####.##.", + ".##..##..##.", + ".##.#..#.##.", + ".##.####.##.", + ".##.####.##.", + ".##.####.##.", + ".##.####.##.", + ".##.####.##.", + ".##########.", + "............" +}; + +static const char* XPM_MEMBER[] = { + "12 12 3 1", + ". c #000000", + "a c #0000c0", + "# c #c0c0ff", + "............", + ".##########.", + ".##########.", + ".##########.", + ".##a.##.a##.", + ".##.a..a.##.", + ".##.#aa#.##.", + ".##.####.##.", + ".##.####.##.", + ".##########.", + ".##########.", + "............" +}; + +static const char* XPM_ENUM[] = { + "12 12 2 1", + ". c #000000", + "# c #ff00ff", + "............", + ".##########.", + ".##########.", + ".##......##.", + ".##.#######.", + ".##.#######.", + ".##.....###.", + ".##.#######.", + ".##.#######.", + ".##......##.", + ".##########.", + "............" +}; + +static const char* XPM_ENUMERATOR[] = { + "12 12 2 1", + ". c #000000", + "# c #ffc0c0", + "............", + ".##########.", + ".##########.", + ".###...####.", + ".##.###.###.", + ".##.###.###.", + ".##.....###.", + ".##.#######.", + ".##.###.###.", + ".###...####.", + ".##########.", + "............" +}; + +static const char* XPM_TYPEDEF[] = { + "12 12 2 1", + ". c #000000", + "# c #c0ffc0", + "............", + ".##########.", + ".#.......##.", + ".####.#####.", + ".####.#####.", + ".####.#####.", + ".####.#####.", + ".####.#####.", + ".####.#####.", + ".####.#####.", + ".##########.", + "............" +}; + +static const char* XPM_LABEL[] = { + "12 12 2 1", + ". c #000000", + "# c #c0ff00", + "............", + ".##########.", + ".#.########.", + ".#.########.", + ".#.########.", + ".#.########.", + ".#.########.", + ".#.########.", + ".#.########.", + ".#.......##.", + ".##########.", + "............" +}; + +static const char* XPM_INCLUDE[] = { + "12 12 2 1", + ". c #000000", + "# c #c0c0c0", + "............", + ".##########.", + ".##.....###.", + ".####.#####.", + ".####.#####.", + ".####.#####.", + ".####.#####.", + ".####.#####.", + ".####.#####.", + ".##.....###.", + ".##########.", + "............" +}; + +static const char* XPM_UNKNOWN[] = { + "12 12 2 1", + ". c #000000", + "# c #ffffff", + "............", + ".##########.", + ".##.....###.", + ".#.#####.##.", + ".########.#.", + ".########.#.", + ".#######.##.", + ".######.###.", + ".####.#####.", + ".##########.", + ".####.#####.", + "............" +}; + +/** + * Class constructor. + */ +KScopePixmaps::KScopePixmaps() : + m_pPixArray(NULL), + m_loader() +{ +} + +/** + * Class destructor. + */ +KScopePixmaps::~KScopePixmaps() +{ + int i; + + for (i = 0; i < PIX_ARRAY_SIZE; i++) + delete m_pPixArray[i]; + + delete [] m_pPixArray; +} + +/** + * Creates the array of embedded pixmaps. + * This function is separated from the constructor since QPixmap objects + * cannot be created at the time the static KScopePixmaps object is + * allocated. + */ +void KScopePixmaps::init() +{ + // Create the pixmap array + m_pPixArray = new QPixmap * [PIX_ARRAY_SIZE]; + + // Create all pixmaps + m_pPixArray[SymFunc] = new QPixmap(XPM_FUNC); + m_pPixArray[SymVar] = new QPixmap(XPM_VAR); + m_pPixArray[SymStruct] = new QPixmap(XPM_STRUCT); + m_pPixArray[SymMacro] = new QPixmap(XPM_MACRO); + m_pPixArray[SymMember] = new QPixmap(XPM_MEMBER); + m_pPixArray[SymEnum] = new QPixmap(XPM_ENUM); + m_pPixArray[SymEnumerator] = new QPixmap(XPM_ENUMERATOR); + m_pPixArray[SymTypedef] = new QPixmap(XPM_TYPEDEF); + m_pPixArray[SymLabel] = new QPixmap(XPM_LABEL); + m_pPixArray[SymInclude] = new QPixmap(XPM_INCLUDE); + m_pPixArray[SymUnknown] = new QPixmap(XPM_UNKNOWN); +} + +/** + * Returns a reference to an embedded pixmap. + * @param name The pixmap's identifier + * @return A reference to the requested pixmap + */ +const QPixmap& KScopePixmaps::getPixmap(PixName name) const +{ + return *m_pPixArray[name]; +} + +/** + * Loads a pixmap with the KIconLoader mechanism. + * @param name The pixmap's identifier + * @return The requested pixmap + */ +QPixmap KScopePixmaps::getPixmap(LoadPixName name) +{ + switch (name) { + case TabUnlocked: + return m_loader.loadIcon("query_unlocked", KIcon::Small, 0, + false); + + case TabLocked: + return m_loader.loadIcon("query_locked", KIcon::Small, 0, + false); + + case TabBookmark: + return m_loader.loadIcon("bookmark", KIcon::Small, 0, + false); + + case TabRW: + return m_loader.loadIcon("file_rw", KIcon::Small, 0, + false); + + case TabRO: + return m_loader.loadIcon("file_ro", KIcon::Small, 0, + false); + + case TabSave: + return m_loader.loadIcon("file_save", KIcon::Small, 0, + false); + + case TabFileList: + return m_loader.loadIcon("view_detailed", KIcon::Small, 0, + false); + + case TabFileTree: + return m_loader.loadIcon("view_tree", KIcon::Small, 0, + false); + + case TabList: + return m_loader.loadIcon("tab_list", KIcon::Small, 0, + false); + + case ButtonSaveAs: + return m_loader.loadIcon("filesaveas", KIcon::Toolbar, + 0, false); + + case ButtonZoomIn: + return m_loader.loadIcon("viewmag+", KIcon::Toolbar, + 0, false); + + case ButtonZoomOut: + return m_loader.loadIcon("viewmag-", KIcon::Toolbar, + 0, false); + + case ButtonRotate: + return m_loader.loadIcon("rotate", KIcon::Toolbar, + 0, false); + + case ButtonPref: + return m_loader.loadIcon("configure", KIcon::Toolbar, + 0, false); + + case CalledTree: + return m_loader.loadIcon("called_tree", KIcon::Toolbar, + 0, false); + + case CallingTree: + return m_loader.loadIcon("calling_tree", KIcon::Toolbar, + 0, false); + + case CallGraph: + return m_loader.loadIcon("call_graph", KIcon::Toolbar, + 0, false); + } + + return QPixmap(); +} + +/** + * @return A reference to a global KScopePixmaps object + */ +KScopePixmaps& Pixmaps() +{ + static KScopePixmaps pix; + return pix; +} diff --git a/src/kscopepixmaps.h b/src/kscopepixmaps.h new file mode 100644 index 0000000..e5f321b --- /dev/null +++ b/src/kscopepixmaps.h @@ -0,0 +1,77 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef KSCOPEPIXMAPS_H +#define KSCOPEPIXMAPS_H + +#include <qpixmap.h> +#include <kiconloader.h> + +#define GET_PIXMAP(_pix) \ + Pixmaps().getPixmap(KScopePixmaps::_pix) + +/** + * Handles all pixmaps required by KScope. + * There are two types of pixmaps: embedded, i.e., pixmaps whose pixels are + * given by static two-dimensional arrays, and loadable, which are retrieved + * through the KIconLoader mechanism. + * The application uses a single global instance of this class. + * @author Elad Lahav + */ + +class KScopePixmaps +{ +public: + KScopePixmaps(); + ~KScopePixmaps(); + + /** Identifiers for embedded pixmaps. */ + enum PixName { SymFunc, SymVar, SymStruct, SymMacro, SymMember, SymEnum, + SymEnumerator, SymTypedef, SymLabel, SymInclude, SymUnknown, + PIX_ARRAY_SIZE }; + + /** Identifiers for loadable pixmaps. */ + enum LoadPixName { TabUnlocked, TabLocked, TabBookmark, TabRW, TabRO, + TabSave, TabFileList, TabFileTree, TabList, ButtonSaveAs, ButtonZoomIn, + ButtonZoomOut, ButtonRotate, ButtonPref, CalledTree, + CallingTree, CallGraph }; + + void init(); + const QPixmap& getPixmap(PixName name) const; + QPixmap getPixmap(LoadPixName name); + +private: + /** An array of pointers to the embedded pixmaps. */ + QPixmap** m_pPixArray; + + /** An icon loader used to retrieve pixmaps through the KDE mechanism. */ + KIconLoader m_loader; +}; + +extern KScopePixmaps& Pixmaps(); + +#endif diff --git a/src/kscopeui.rc b/src/kscopeui.rc new file mode 100644 index 0000000..81d3646 --- /dev/null +++ b/src/kscopeui.rc @@ -0,0 +1,141 @@ +<!DOCTYPE kpartgui> +<kpartgui name="kscope"> +<MenuBar> + <Menu name="file"><text>&File</text> + <Merge/> + </Menu> + <Menu name="edit"><text>&Edit</text> + <Action name="edit_external_editor"/> + <Separator/> + <Merge/> + <Action name="edit_goto_tag"/> + <Action name="edit_comp_symbol"/> + </Menu> + <Menu name="view"><text>&View</text> + <Action name="view_toggle_filelist_dock"/> + <Action name="view_toggle_query_dock"/> + <Action name="view_toggle_tag_list"/> + <Separator/> + <Merge/> + </Menu> + <Menu name="project"><text>&Project</text> + <Action name="project_new"/> + <Action name="project_open"/> + <Action name="project_cscope_out"/> + <Action name="project_add_rem_files"/> + <Action name="project_properties"/> + <Separator/> + <Action name="project_make"/> + <Action name="project_remake"/> + <Separator/> + <Action name="project_close"/> + </Menu> + <Menu name="cscope"><text>&Cscope</text> + <Action name="cscope_rebuild"/> + <Separator/> + <Action name="cscope_references"/> + <Action name="cscope_definition"/> + <Action name="cscope_called"/> + <Action name="cscope_calling"/> + <Action name="cscope_text"/> + <Action name="cscope_pattern"/> + <Action name="cscope_file"/> + <Action name="cscope_including"/> + <Separator/> + <Action name="cscope_quick_def"/> + <Action name="cscope_call_tree"/> + </Menu> + <Menu name="go"><text>&Go</text> + <Action name="go_prev_result"/> + <Action name="go_next_result"/> + <Separator/> + <Action name="go_prev_pos"/> + <Action name="go_next_pos"/> + <Action name="go_history"/> + <Separator/> + <Action name="go_bookmarks"/> + </Menu> + <Merge/> + <Menu name="window"><text>&Window</text> + <Action name="window_close_all"/> + <Action name="window_go_left"/> + <Action name="window_go_right"/> + <Separator/> + </Menu> + <Menu name="settings"><text>&Settings</text> + <Merge/> + </Menu> + <Menu name="help"><text>&Help</text> + <Action name="help_welcome"/> + <Merge/> + </Menu> +</MenuBar> +<Menu name="query_popup"><text>&Query</text> + <Action name="query_new"/> + <Action name="query_refresh"/> + <Action name="query_toggle_locked"/> + <Separator/> + <Action name="query_close"/> +</Menu> +<Menu name="ktexteditor_popup"> + <Merge/> + <Menu name="cscope"><text>Cscope</text> + <Action name="cscope_references"/> + <Action name="cscope_definition"/> + <Action name="cscope_called"/> + <Action name="cscope_calling"/> + <Action name="cscope_text"/> + <Action name="cscope_pattern"/> + <Action name="cscope_file"/> + <Action name="cscope_including"/> + <Separator/> + <Action name="cscope_quick_def"/> + <Action name="cscope_call_tree"/> + </Menu> + <Action name="edit_external_editor"/> + <Action name="file_close"/> +</Menu> +<Menu name="kscope_popup"> + <Action name="cscope_references"/> + <Action name="cscope_definition"/> + <Action name="cscope_called"/> + <Action name="cscope_calling"/> + <Action name="cscope_text"/> + <Action name="cscope_pattern"/> + <Action name="cscope_file"/> + <Action name="cscope_including"/> + <Separator/> + <Action name="cscope_quick_def"/> + <Action name="cscope_call_tree"/> + <Action name="edit_external_editor"/> + <Separator/> + <Action name="file_close"/> +</Menu> +<ToolBar fullWidth="true" name="mainToolBar"> + <Action name="file_close"/> +</ToolBar> +<ToolBar fullWidth="true" name="projectToolBar"><text>Project</text> + <Action name="project_open"/> + <Action name="cscope_rebuild"/> +</ToolBar> +<ToolBar fullWidth="true" name="navigateToolBar"><text>Navigation</text> + <Action name="go_prev_pos"/> + <Action name="go_next_pos"/> + <Action name="go_history"/> + <Action name="go_bookmarks"/> +</ToolBar> +<ToolBar fullWidth="true" name="queryToolBar"><text>Query</text> + <Action name="query_new"/> + <Action name="query_refresh"/> + <Action name="query_toggle_locked"/> + <Action name="query_close"/> + <Separator/> + <Action name="go_prev_result"/> + <Action name="go_next_result"/> +</ToolBar> +<ToolBar fullWidth="true" name="workspaceToolBar"><text>Workspace</text> + <Action name="view_toggle_tag_list"/> + <Action name="view_toggle_query_dock"/> + <Action name="view_toggle_filelist_dock"/> +</ToolBar> +</kpartgui> diff --git a/src/lo16-app-kscope.png b/src/lo16-app-kscope.png Binary files differnew file mode 100644 index 0000000..fd8ef48 --- /dev/null +++ b/src/lo16-app-kscope.png diff --git a/src/lo32-app-kscope.png b/src/lo32-app-kscope.png Binary files differnew file mode 100644 index 0000000..5bb28f8 --- /dev/null +++ b/src/lo32-app-kscope.png diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..ffa7b55 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,97 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <kcmdlineargs.h> +#include <kaboutdata.h> +#include <klocale.h> + +#include "kscope.h" +#include "kscopeconfig.h" + +static const char *description = + I18N_NOOP("KScope\nA source-editing environment for KDE, based on " + "Cscope"); + +static KCmdLineOptions options[] = +{ + { "+[CSCOPE.OUT path]", + I18N_NOOP("Opens a cscope.out file in a temporary project"), 0 }, + { "+[CSCOPE.PROJ path | KScope project directory path]", + I18N_NOOP("Opens a KScope project"), 0 }, + KCmdLineLastOption +}; + +/** + * Defines the programme's entry point. + * Creates KScope's main window, and starts the event loop. + * @param argc Number of command line arguments + * @param argv Command line arguments + * @return Programme's exit value + */ +int main(int argc, char *argv[]) +{ + // Create the "About" dialogue + KAboutData aboutData( "kscope", I18N_NOOP("KScope"), + VERSION, description, KAboutData::License_BSD, + "(c) 2003-2007, Elad Lahav", 0, "http://kscope.sourceforge.net", + "elad_lahav@users.sf.net"); + aboutData.addAuthor("Elad Lahav", "Developer", + "elad_lahav@users.sf.net"); + aboutData.addCredit("Albert Yosher", + "Code completion, patches and bug fixes", "ayosher@users.sf.net"); + aboutData.addCredit("Gabor Fekete", "Bug fixes and patches", + "feketgai@index.hu"); + + // Initialise command-line argument parsing + KCmdLineArgs::init(argc, argv, &aboutData); + KCmdLineArgs::addCmdLineOptions(options); + + // Parse command line arguments + KCmdLineArgs* pArgs = KCmdLineArgs::parsedArgs(); + + // Create the main window + KApplication a; + KScope* pKScope = new KScope(); + a.setMainWidget(pKScope); + + // Display the main window + pKScope->show(); + + // Handle command-line arguments + if (pArgs->count() > 0) { + pKScope->parseCmdLine(pArgs); + } else if (Config().getLoadLastProj()) { + // No arguments given, load the most recent project + pKScope->openLastProject(); + } + + // Make sure Cscope is properly installed + pKScope->verifyCscope(); + + // Start the event loop + return a.exec(); +} diff --git a/src/makedlg.cpp b/src/makedlg.cpp new file mode 100644 index 0000000..ac60129 --- /dev/null +++ b/src/makedlg.cpp @@ -0,0 +1,267 @@ +/*************************************************************************** + * + * Copyright (C) 2006 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qtextcodec.h> +#include <ktextbrowser.h> +#include <kcombobox.h> +#include <kurlrequester.h> +#include <kmessagebox.h> +#include <klocale.h> +#include "makedlg.h" +#include "makefrontend.h" +#include "queryview.h" + +/** Window flags for call-tree widgets. */ +#define MAKE_DLG_W_FLAGS \ + WStyle_Customize | \ + WStyle_NormalBorder | \ + WStyle_Title + +/** + * Class constructor. + * @param pParent The parent widget + * @param szName The widget's name + */ +MakeDlg::MakeDlg(QWidget* pParent, const char* szName) : + MakeLayout(pParent, szName, MAKE_DLG_W_FLAGS) +{ + // Don't show the "Function" column + m_pErrorView->setColumnWidthMode(0, QListView::Manual); + m_pErrorView->setColumnWidth(0, 0); + + // Create a new make front-end + m_pMake = new MakeFrontend(); + connect(m_pMake, SIGNAL(dataReady(FrontendToken*)), this, + SLOT(slotShowOutput(FrontendToken*))); + connect(m_pMake, SIGNAL(finished(uint)), this, SLOT(slotFinished(uint))); + connect(m_pMake, + SIGNAL(error(const QString&, const QString&, const QString&)), + this, + SLOT(slotAddError(const QString&, const QString&, const QString&))); + + // The Root URL control should browse directories + m_pRootURL->setMode(KFile::Directory); + + // Handle URL links in the browser + m_pOutputBrowser->setNotifyClick(true); + connect(m_pOutputBrowser, SIGNAL(urlClick(const QString&)), this, + SLOT(slotBrowserClicked(const QString&))); + + // Handle selections in the error view + connect(m_pErrorView, SIGNAL(lineRequested(const QString& , uint)), this, + SIGNAL(fileRequested(const QString&, uint))); + + // Do not allow duplicates in the command history + m_pCommandHistory->setDuplicatesEnabled(false); +} + +/** + * Class destructor. + */ +MakeDlg::~ MakeDlg() +{ + delete m_pMake; +} + +/** + * @return The currently set make command + */ +QString MakeDlg::getCommand() const +{ + return m_pCommandHistory->currentText(); +} + +/** + * @param sCmd The new make command to use + */ +void MakeDlg::setCommand(const QString& sCmd) +{ + m_pCommandHistory->setCurrentText(sCmd); + m_pCommandHistory->addToHistory(sCmd); +} + +/** + * @return The directory in which to run the make command + */ +QString MakeDlg::getDir() const +{ + return m_pRootURL->url(); +} + +/** + * @param sURL The new root directory to use + */ +void MakeDlg::setDir(const QString& sURL) +{ + m_pRootURL->setURL(sURL); +} + +/** + * Overrides the default close behaviour. + * Makes sure that a window is not closed while a make process is running, + * unless the user explicitly requests it. In this case, the make process + * is killed. + * @param pEvent The close event descriptor + */ +void MakeDlg::closeEvent(QCloseEvent* pEvent) +{ + // Check if a process is currently running + if (m_pMake->isRunning()) { + // Prompt the user + switch (KMessageBox::questionYesNoCancel(this, + i18n("A make process is running. Would you like to stop it first?"), + i18n("Close Make Window"))) { + case KMessageBox::Yes: + // Stop the process first + m_pMake->kill(); + break; + + case KMessageBox::No: + // Do nothing + break; + + case KMessageBox::Cancel: + // Abort closing + pEvent->ignore(); + return; + } + } + + QWidget::closeEvent(pEvent); +} + +/** + * Starts a make process using the user-supplied command. + * This slot is connected to the clicked() signal of the "Make" button. + */ +void MakeDlg::slotMake() +{ + QString sCommand; + + // Clear the current contents + m_pOutputBrowser->clear(); + m_pErrorView->clear(); + + // Run the make command + sCommand = m_pCommandHistory->currentText(); + if (!m_pMake->run("make", QStringList::split(" ", sCommand), + m_pRootURL->url())) { + KMessageBox::error(this, m_pMake->getRunError()); + return; + } + + // Add the command to the command history + m_pCommandHistory->addToHistory(sCommand); + + // Disbale the make button + m_pMakeButton->setEnabled(false); + m_pStopButton->setEnabled(true); +} + +/** + * Terminates the current make process. + * This slot is connected to the clicked() signal of the stop button. + */ +void MakeDlg::slotStop() +{ + m_pMake->kill(); +} + +/** + * Displays the parsed output, as generated by the MakeFrontend object. + * This slot is connected to the dataReady() signal of the make front-end. + * @param pToken Holds the parsed data + */ +void MakeDlg::slotShowOutput(FrontendToken* pToken) +{ + QString sData; + + // GCC uses unicode quote characters - this should ensure that they are + // treated correctly by the text browser widget + sData = QTextCodec::codecForLocale()->toUnicode(pToken->getData()); + m_pOutputBrowser->append(sData); +} + +/** + * Displays the results of the make command. + * This slot is connected to the finished() signal of the make front-end. + */ +void MakeDlg::slotFinished(uint) +{ + // Add "Success" or "Error" at the end of the output + if (m_pMake->exitStatus() == 0) { + m_pOutputBrowser->append("<font color=\"#008000\"><b>Success</b>" + "</font>"); + } + else { + m_pOutputBrowser->append("<font color=\"#ff0000\"><b>Error</b></font>"); + } + + // Re-enable the "Make" button + m_pMakeButton->setEnabled(true); + m_pStopButton->setEnabled(false); +} + +/** + * Emits the fileRequested() signal when a browser link is clicked. + * This slot is connected to the urlClick() signal of the browser. + * @param sURL The requested URL + */ +void MakeDlg::slotBrowserClicked(const QString& sURL) +{ + QString sFile; + QString sLine; + + // Exract the file name and the line number from the URL + sFile = sURL.section('&', 0, 0); + sLine = sURL.section('&', 1, 1); + + // Add root path for relative paths + if (!sFile.startsWith("/")) + sFile = m_pRootURL->url() + "/" + sFile; + + // Emit the signal + emit fileRequested(sFile, sLine.toUInt()); +} + +/** + * Show an error/warning on a deidicated list. + * This slot is connected to the error() signal of the make front-end. + * @param sFile The file name containing the error/warning + * @param sLine The line number + * @param sText An explanation of the error + */ +void MakeDlg::slotAddError(const QString& sFile, const QString& sLine, + const QString& sText) +{ + QString sUniText; + + sUniText = QTextCodec::codecForLocale()->toUnicode(sText); + m_pErrorView->addRecord("", sFile, sLine, sUniText); +} + +#include "makedlg.moc" diff --git a/src/makedlg.h b/src/makedlg.h new file mode 100644 index 0000000..3120b95 --- /dev/null +++ b/src/makedlg.h @@ -0,0 +1,78 @@ +/*************************************************************************** + * + * Copyright (C) 2006 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef MAKEDLG_H +#define MAKEDLG_H + +#include "makelayout.h" + +class MakeFrontend; +class FrontendToken; + +/** + * A window that displays the output of make-like commands. + * The window contains a text browser showing errors as links to source + * locations. + * The make process is determined by a user-specified command, and is run in + * a user-specified directory. Controls are provided for modifying these values. + * @author Elad Lahav + */ +class MakeDlg: public MakeLayout +{ + Q_OBJECT + +public: + MakeDlg(QWidget* pParent = 0, const char* szName = 0); + virtual ~MakeDlg(); + + QString getCommand() const; + void setCommand(const QString&); + QString getDir() const; + void setDir(const QString&); + +public slots: + virtual void slotMake(); + +signals: + void fileRequested(const QString&, uint); + +protected: + virtual void closeEvent(QCloseEvent*); + +protected slots: + virtual void slotStop(); + void slotShowOutput(FrontendToken*); + void slotFinished(uint); + void slotBrowserClicked(const QString&); + void slotAddError(const QString&, const QString&, const QString&); + +private: + /** Handles the make process. */ + MakeFrontend* m_pMake; +}; + +#endif diff --git a/src/makefrontend.cpp b/src/makefrontend.cpp new file mode 100644 index 0000000..80ea9b8 --- /dev/null +++ b/src/makefrontend.cpp @@ -0,0 +1,134 @@ +/*************************************************************************** + * + * Copyright (C) 2006 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qregexp.h> +#include "makefrontend.h" + +// TODO: +// This should probably be configurable on a per-project basis. +#define PATH_ELEM "[a-zA-Z0-9_\\.\\-]+" + +#define RE_FILE_LINE \ + "((?:\\/)?(?:"PATH_ELEM"\\/)*"PATH_ELEM"):([0-9]+)(:[0-9]+)?: (.*)" +#define RE_ENTER_DIR \ + "Entering directory " \ + "\\`((\\/)?("PATH_ELEM"\\/)*"PATH_ELEM")" +#define RE_EXIT_DIR "Leaving directory" + +/** + * Class constructor. + * @param bAutoDelete If true, the object is deleted when the process + * terminates (false by default) + */ +MakeFrontend::MakeFrontend(bool bAutoDelete) : Frontend(1, bAutoDelete) +{ + // Execute inside a shell + setUseShell(true); + + // Each token represent a complete line + m_delim = Newline; +} + +/** + * Class destructor. + */ +MakeFrontend::~MakeFrontend() +{ +} + +/** + * Executes the make command. + * @param sName The name of the process (for error messages) + * @param slArgs A list containing the command-line arguments + * @param sWorkDir Initial build directory + * @param bBlock (Optional) true to block, false otherwise + * @return true if the process was executed successfully, false otherwise + */ +bool MakeFrontend::run(const QString& sName, const QStringList& slArgs, + const QString& sWorkDir, bool bBlock) +{ + QStringList slShellArgs; + + // Store the current build directory + m_slPathStack.push_back(sWorkDir); + + // Join the output streams, so that they can both be parsed by + // parseStdout() + slShellArgs = slArgs; + slShellArgs << "2>&1"; + + // Execute the command + return Frontend::run(sName, slShellArgs, sWorkDir, bBlock); +} + +/** + * Parses lines of output produced by the make command. + * @param sToken A single line of output + */ +Frontend::ParseResult MakeFrontend::parseStdout(QString& sToken, ParserDelim) +{ + static QRegExp reErrWarn(RE_FILE_LINE); + static QRegExp reEntDir(RE_ENTER_DIR); + static QRegExp reExtDir(RE_EXIT_DIR); + QString sRep; + int nPos; + QString sFile, sLine, sText; + + if ((nPos = reErrWarn.search(sToken)) >= 0) { + // An error/warning message + if (sToken.at(nPos) == '/') { + sFile = reErrWarn.capturedTexts()[1]; + } + else { + sFile = m_slPathStack.last() + "/" + + reErrWarn.capturedTexts()[1]; + } + + sLine = reErrWarn.capturedTexts()[2]; + sText = reErrWarn.capturedTexts()[4]; + emit error(sFile, sLine, sText); + + sRep = QString("<a href=\"") + sFile + "&\\2\">\\1:\\2</a>\\3: \\4"; + sToken.replace(reErrWarn, sRep); + } + else if ((nPos = reEntDir.search(sToken)) >= 0) { + // Recursing into a directory + m_slPathStack.push_back(reEntDir.capturedTexts()[1]); + sToken = QString("<b>Entering directory</b> ") + + m_slPathStack.last(); + } + else if ((nPos = reExtDir.search(sToken)) >= 0) { + // Leaving a directory + sToken = QString("<b>Leaving directory</b> ") + + m_slPathStack.last(); + m_slPathStack.pop_back(); + } + + return RecordReady; +} + +#include "makefrontend.moc" diff --git a/src/makefrontend.h b/src/makefrontend.h new file mode 100644 index 0000000..4ed575f --- /dev/null +++ b/src/makefrontend.h @@ -0,0 +1,61 @@ +/*************************************************************************** + * + * Copyright (C) 2006 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef MAKEFRONTEND_H +#define MAKEFRONTEND_H + +#include <frontend.h> + +/** + * A shell-process front-end intended for running make-like tasks. + * Records are single-line tokens delimited by newline characters. The parser + * replaces references to source lines (e.g., filename:123) with hypertext + * links for use in a browser. + * @author Elad Lahav + */ +class MakeFrontend : public Frontend +{ + Q_OBJECT + +public: + MakeFrontend(bool bAutoDelete = false); + ~MakeFrontend(); + + virtual bool run(const QString&, const QStringList&, + const QString&, bool bBlock = false); + virtual ParseResult parseStdout(QString&, ParserDelim); + +signals: + void error(const QString& sFile, const QString& sLine, + const QString& sText); + +private: + /** A stack of paths used to track the current build directory. */ + QStringList m_slPathStack; +}; + +#endif diff --git a/src/makelayout.ui b/src/makelayout.ui new file mode 100644 index 0000000..e47acf8 --- /dev/null +++ b/src/makelayout.ui @@ -0,0 +1,245 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>MakeLayout</class> +<widget class="QWidget"> + <property name="name"> + <cstring>MakeLayout</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>768</width> + <height>642</height> + </rect> + </property> + <property name="caption"> + <string>KScope - Make</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout10</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Root Directory:</string> + </property> + </widget> + <widget class="KHistoryCombo" row="1" column="1"> + <property name="name"> + <cstring>m_pCommandHistory</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="KURLRequester" row="0" column="1"> + <property name="name"> + <cstring>m_pRootURL</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Command:</string> + </property> + </widget> + </grid> + </widget> + <widget class="QTabWidget"> + <property name="name"> + <cstring>tabWidget2</cstring> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>Output</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KTextBrowser"> + <property name="name"> + <cstring>m_pOutputBrowser</cstring> + </property> + </widget> + </vbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>Errors a&nd Warnings</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QueryView"> + <property name="name"> + <cstring>m_pErrorView</cstring> + </property> + </widget> + </vbox> + </widget> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout7</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>520</width> + <height>21</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pMakeButton</cstring> + </property> + <property name="text"> + <string>&Make</string> + </property> + <property name="accel"> + <string>Alt+M</string> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pStopButton</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>&Stop</string> + </property> + <property name="accel"> + <string>Alt+S</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pCloseButton</cstring> + </property> + <property name="text"> + <string>&Close</string> + </property> + <property name="accel"> + <string>Alt+C</string> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<customwidgets> + <customwidget> + <class>QueryView</class> + <header location="local">queryview.h</header> + <sizehint> + <width>-1</width> + <height>-1</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>5</hordata> + <verdata>5</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + </customwidget> +</customwidgets> +<images> + <image name="image0"> + <data format="XBM.GZ" length="79">789c534e494dcbcc4b554829cdcdad8c2fcf4c29c95030e0524611cd48cd4ccf28010a1797249664262b2467241641a592324b8aa363156c15aab914146aadb90067111b1f</data> + </image> +</images> +<connections> + <connection> + <sender>m_pCloseButton</sender> + <signal>clicked()</signal> + <receiver>MakeLayout</receiver> + <slot>close()</slot> + </connection> + <connection> + <sender>m_pMakeButton</sender> + <signal>clicked()</signal> + <receiver>MakeLayout</receiver> + <slot>slotMake()</slot> + </connection> + <connection> + <sender>m_pStopButton</sender> + <signal>clicked()</signal> + <receiver>MakeLayout</receiver> + <slot>slotStop()</slot> + </connection> + <connection> + <sender>m_pCommandHistory</sender> + <signal>returnPressed()</signal> + <receiver>MakeLayout</receiver> + <slot>slotMake()</slot> + </connection> +</connections> +<tabstops> + <tabstop>m_pRootURL</tabstop> + <tabstop>m_pCommandHistory</tabstop> + <tabstop>m_pMakeButton</tabstop> + <tabstop>m_pStopButton</tabstop> + <tabstop>m_pCloseButton</tabstop> +</tabstops> +<slots> + <slot access="protected">slotStop()</slot> + <slot access="protected">slotMake()</slot> +</slots> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kcombobox.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kurlrequester.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>ktextbrowser.h</includehint> + <includehint>queryview.h</includehint> +</includehints> +</UI> diff --git a/src/newprojectdlg.cpp b/src/newprojectdlg.cpp new file mode 100644 index 0000000..ec8fbca --- /dev/null +++ b/src/newprojectdlg.cpp @@ -0,0 +1,354 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qregexp.h> +#include <qpushbutton.h> +#include <qspinbox.h> +#include <qlabel.h> +#include <qtextedit.h> +#include <kurlrequester.h> +#include <klineedit.h> +#include <kmessagebox.h> +#include <klocale.h> +#include "newprojectdlg.h" + +/** + * Class constructor. + * @param bNewProj true to create a new project dialog, false to display + * the properties of an existing project + * @param pParent The parent widget + * @param szName The widget's name + */ +NewProjectDlg::NewProjectDlg(bool bNewProj, QWidget* pParent, + const char* szName) : + NewProjectLayout(pParent, szName), + m_bNewProj(bNewProj) +{ + ProjectBase::Options opt; + + // Create the auto-completion sub-dialogue + m_pAutoCompDlg = new AutoCompletionDlg(this); + + // Restrict the path requester to existing directories. + m_pPathRequester->setMode(KFile::Directory | KFile::ExistingOnly | + KFile::LocalOnly); + m_pSrcRootRequester->setMode(KFile::Directory | KFile::ExistingOnly | + KFile::LocalOnly); + + // Set up the Create/Cancel buttons + connect(m_pCreateButton, SIGNAL(clicked()), this, SLOT(accept())); + connect(m_pCancelButton, SIGNAL(clicked()), this, SLOT(reject())); + + // Show the auto-completion properties dialogue + connect(m_pACButton, SIGNAL(clicked()), m_pAutoCompDlg, SLOT(exec())); + + // Perform actions specific to the type of dialog (new project or + // project properties) + if (bNewProj) { + // Set default project properties + ProjectBase::getDefOptions(opt); + setProperties("", "", opt); + } + else { + // Give appropriate titles to the dialog and the accept button + setCaption(i18n("Project Properties")); + m_pCreateButton->setText(i18n("OK")); + + // Disable the non-relevant widgets + m_pNameEdit->setEnabled(false); + m_pPathRequester->setEnabled(false); + } +} + +/** + * Class destructor. + */ +NewProjectDlg::~NewProjectDlg() +{ +} + +/** + * Configures the dialog's widget to display the properties of the current + * project. + * @param sName The project's name + * @param sPath The project's path + * @param opt Project parameters configurable in this dialogue + */ +void NewProjectDlg::setProperties(const QString& sName, const QString& sPath, + const ProjectBase::Options& opt) +{ + QStringList::ConstIterator itr; + + // Set values for current project + m_pNameEdit->setText(sName); + m_pPathRequester->setURL(sPath); + m_pSrcRootRequester->setURL(opt.sSrcRootPath); + m_pKernelCheck->setChecked(opt.bKernel); + m_pInvCheck->setChecked(opt.bInvIndex); + m_pNoCompCheck->setChecked(opt.bNoCompress); + m_pSlowPathCheck->setChecked(opt.bSlowPathDef); + + if (opt.nAutoRebuildTime >= 0) { + m_pAutoRebuildCheck->setChecked(true); + m_pAutoRebuildSpin->setValue(opt.nAutoRebuildTime); + } + + if (opt.bACEnabled) { + m_pACCheck->setChecked(true); + } + + if (opt.nTabWidth > 0) { + m_pTabWidthCheck->setChecked(true); + m_pTabWidthSpin->setValue(opt.nTabWidth); + } + + // Initialise the auto-completion sub-dialogue + m_pAutoCompDlg->m_nMinChars = opt.nACMinChars; + m_pAutoCompDlg->m_nDelay = opt.nACDelay; + m_pAutoCompDlg->m_nMaxEntries = opt.nACMaxEntries; + + // Add type strings to the types list box + for (itr = opt.slFileTypes.begin(); itr != opt.slFileTypes.end(); ++itr) + m_pTypesList->insertItem(*itr); + + m_pCtagsCmdEdit->setText(opt.sCtagsCmd); +} + +/** + * Retrieves the text entered by the user in the dialog's "Project Name" edit + * box. + * @return The name of the new project + */ +QString NewProjectDlg::getName() +{ + return m_pNameEdit->text(); +} + +/** + * Retrieves the text entered by the user in the dialog's "Project Path" edit + * box. + * Note that the chosen path will be the parent of the new project's + * directory, created under it using the project's name. + * @return The full path of the parent directory for the new project + */ +QString NewProjectDlg::getPath() +{ + if (m_pHiddenDirCheck->isChecked()) + return QString(m_pSrcRootRequester->url()) + "/.cscope"; + + return m_pPathRequester->url(); +} + +/** + * Fills a structure with all user-configured project options. + * @param opt The structure to fill + */ +void NewProjectDlg::getOptions(ProjectBase::Options& opt) +{ + opt.sSrcRootPath = m_pSrcRootRequester->url(); + opt.slFileTypes = m_slTypes; + opt.bKernel = m_pKernelCheck->isChecked(); + opt.bInvIndex = m_pInvCheck->isChecked(); + opt.bNoCompress = m_pNoCompCheck->isChecked(); + opt.bSlowPathDef = m_pSlowPathCheck->isChecked(); + + if (m_pAutoRebuildCheck->isChecked()) + opt.nAutoRebuildTime = m_pAutoRebuildSpin->value(); + else + opt.nAutoRebuildTime = -1; + + if (m_pTabWidthCheck->isChecked()) + opt.nTabWidth = m_pTabWidthSpin->value(); + else + opt.nTabWidth = 0; + + opt.bACEnabled = m_pACCheck->isChecked(); + opt.nACMinChars = m_pAutoCompDlg->m_nMinChars; + opt.nACDelay = m_pAutoCompDlg->m_nDelay; + opt.nACMaxEntries = m_pAutoCompDlg->m_nMaxEntries; + + opt.sCtagsCmd = m_pCtagsCmdEdit->text(); +} + +/** + * Ends the dialog after the user has clicked the "OK" button. + */ +void NewProjectDlg::accept() +{ + int i, nCount; + + // Validate the name of a new project + if (m_bNewProj) { + QRegExp re("[^ \\t\\n]+"); + if (!re.exactMatch(m_pNameEdit->text())) { + KMessageBox::error(0, i18n("Project names must not contain " + "whitespace.")); + return; + } + } + + // Fill the string list with all file types + nCount = (int)m_pTypesList->count(); + for (i = 0; i < nCount; i++) + m_slTypes.append(m_pTypesList->text(i)); + + // Clean-up the source root + QDir dir(m_pSrcRootRequester->url()); + if (dir.exists()) + m_pSrcRootRequester->setURL(dir.absPath()); + else + m_pSrcRootRequester->setURL("/"); + + // Close the dialog + QDialog::accept(); +} + +/** + * Adds the the file type string in the edit-box to the list of project types. + * This slot is called when the "Add.." button is clicked. + */ +void NewProjectDlg::slotAddType() +{ + QString sType; + + // Try the custom type edit-box first. + sType = m_pTypesEdit->text(); + sType.stripWhiteSpace(); + if (sType.isEmpty()) + return; + + // Validate the type string + QRegExp reg("[ \\t\\n\\|\\\\\\/]"); + if (sType.contains(reg)) { + KMessageBox::error(0, i18n("This is not a valid file type!")); + return; + } + + // Do not add an existing type. + if (m_pTypesList->findItem(sType, Qt::CaseSensitive | Qt::ExactMatch) != + NULL) { + return; + } + + // Add the file type to the list + m_pTypesList->insertItem(sType); + m_pTypesEdit->clear(); +} + +/** + * Removes the selected item from the list of file types. + * This slot is called when the "Remove" button is clicked. + */ +void NewProjectDlg::slotRemoveType() +{ + int nItem; + QString sType; + + // Verify an item is selected + nItem = m_pTypesList->currentItem(); + if (nItem == -1) + return; + + // Remove the selected item + sType = m_pTypesList->currentText(); + m_pTypesList->removeItem(nItem); + + // Add to the list of available types. + if (m_pAvailTypesList->findItem(sType, Qt::CaseSensitive | Qt::ExactMatch) + == NULL) { + m_pAvailTypesList->insertItem(sType); + } + +} + +/** + * Changes the text in the types edit-box to reflect the current selection in + * the list of available types. + * This slot is called whenever a new item is highlighted in the list of + * available types. + * @param sType The newly selected type + */ +void NewProjectDlg::slotAvailTypesChanged(const QString& sType) +{ + m_pTypesEdit->setText(sType); +} + +/** + * Class constructor. + * @param pParent The parent widget + * @param szName The widget's name + */ +AutoCompletionDlg::AutoCompletionDlg(QWidget* pParent, + const char* szName ) : + AutoCompletionLayout(pParent, szName) +{ + connect(m_pOKButton, SIGNAL(clicked()), this, SLOT(accept())); + connect(m_pCancelButton, SIGNAL(clicked()), this, SLOT(reject())); +} + +/** + * Class destructor. + */ +AutoCompletionDlg::~AutoCompletionDlg() +{ +} + +/** + * Displays the dialogue, and waits for either the "OK" or "Cancel" button to + * be clicked. + * Before the dialogue is displayed, the stored values are set to the widgets. + * @return The dialogue's termination code + */ +int AutoCompletionDlg::exec() +{ + // Set current values + m_pMinCharsSpin->setValue(m_nMinChars); + m_pDelaySpin->setValue(m_nDelay); + m_pMaxEntriesSpin->setValue(m_nMaxEntries); + + // Show the dialogue + return QDialog::exec(); +} + +/** + * Stores the values set by the user in the dialogue widgets, and terminates + * the dialogue. + * This slot is connected to the clicked() signal of the "OK" button. + */ +void AutoCompletionDlg::accept() +{ + // Store widget values + m_nMinChars = m_pMinCharsSpin->value(); + m_nDelay = m_pDelaySpin->value(); + m_nMaxEntries = m_pMaxEntriesSpin->value(); + + // Close the dialogue, indicating acceptance + QDialog::accept(); +} + + +#include "newprojectdlg.moc" diff --git a/src/newprojectdlg.h b/src/newprojectdlg.h new file mode 100644 index 0000000..5d8e556 --- /dev/null +++ b/src/newprojectdlg.h @@ -0,0 +1,112 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef NEWPROJECTDLG_H +#define NEWPROJECTDLG_H + +#include <qlineedit.h> +#include <qcheckbox.h> +#include <newprojectlayout.h> +#include <autocompletionlayout.h> +#include "projectbase.h" + +/** + * A sub-dialogue of the New Project dialogue. + * Allows the user to configure auto-completion parameters. + * @author Elad Lahav + */ +class AutoCompletionDlg : public AutoCompletionLayout +{ + Q_OBJECT + +public: + AutoCompletionDlg(QWidget* pParent, const char* szName = NULL); + ~AutoCompletionDlg(); + +public slots: + int exec(); + +protected slots: + virtual void accept(); + +private: + /** The minimum number of characters in a symbol required for automatic + completion. */ + uint m_nMinChars; + + /** The time, in seconds, to wait before automatic completion is + attempted. */ + uint m_nDelay; + + /** The maximal number of results. */ + uint m_nMaxEntries; + + friend class NewProjectDlg; +}; + +/** + * A dialog for creating new projects. + * Prompts the user for the project's name, the directory for Cscope's files, + * the types of files included in the project and several options. + * Can also be used to change some of the properties of a project after it + * has been created. + * @author Elad Lahav + */ + +class NewProjectDlg : public NewProjectLayout +{ + Q_OBJECT + +public: + NewProjectDlg(bool, QWidget* pParent = NULL, const char* szName = NULL); + ~NewProjectDlg(); + + void setProperties(const QString&, const QString&, + const ProjectBase::Options&); + + QString getName(); + QString getPath(); + void getOptions(ProjectBase::Options&); + +protected slots: + virtual void accept(); + virtual void slotAddType(); + virtual void slotRemoveType(); + virtual void slotAvailTypesChanged(const QString&); + +private: + /** The file MIME-types associated with the new project. */ + QStringList m_slTypes; + + /** A sub-dialogue for configuring symbol auto-completion parameters. */ + AutoCompletionDlg* m_pAutoCompDlg; + + /** Whether the dialogue represents a new or existing project. */ + bool m_bNewProj; +}; + +#endif diff --git a/src/newprojectlayout.ui b/src/newprojectlayout.ui new file mode 100644 index 0000000..841b059 --- /dev/null +++ b/src/newprojectlayout.ui @@ -0,0 +1,778 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>NewProjectLayout</class> +<widget class="QDialog"> + <property name="name"> + <cstring>NewProjectLayout</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>539</width> + <height>383</height> + </rect> + </property> + <property name="caption"> + <string>Create Project</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QTabWidget"> + <property name="name"> + <cstring>tabWidget2</cstring> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>Detai&ls</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout18</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Path</string> + </property> + </widget> + <widget class="QLineEdit" row="0" column="1"> + <property name="name"> + <cstring>m_pNameEdit</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>Enter a name for this project. +The name must conform to the file system's naming conventions for directories (e.g., no spaces, exclamaion marks, etc.).</string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Name</string> + </property> + </widget> + <widget class="KURLRequester" row="1" column="1"> + <property name="name"> + <cstring>m_pPathRequester</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>The path to hold this project. +KScope will create a directory with the given name under this project, and populate it with the project configuration and database files. +This does not need to be the path in which the source files reside.</string> + </property> + </widget> + </grid> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_pHiddenDirCheck</cstring> + </property> + <property name="text"> + <string>&Use a hidden folder under the source root directory</string> + </property> + <property name="accel"> + <string>Alt+U</string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout19</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>Source Root (Optional)</string> + </property> + </widget> + <widget class="KURLRequester"> + <property name="name"> + <cstring>m_pSrcRootRequester</cstring> + </property> + </widget> + </hbox> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_3</cstring> + </property> + <property name="text"> + <string><blockquote>A project consists of several files located in a directory + with the given name and path. The project's name needs to be a valid directory +name and must not contain any whitespace.</blockquote> +<br> +<blockquote>The Source Root is a convinient way to specify a common +path for all source files, but is not required.</blockquote></string> + </property> + <property name="alignment"> + <set>WordBreak|AlignVCenter</set> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer29</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>31</width> + <height>50</height> + </size> + </property> + </spacer> + </vbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>File T&ypes</string> + </attribute> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox1</cstring> + </property> + <property name="title"> + <string>This Project</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QListBox"> + <property name="name"> + <cstring>m_pTypesList</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>KScope uses these filters to locate source files to include in this project.</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout5</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer7</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>21</width> + <height>61</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pAddButton</cstring> + </property> + <property name="text"> + <string><< &Add</string> + </property> + <property name="accel"> + <string>Alt+A</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Adds the selected file type to the current project.</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pRemoveButton</cstring> + </property> + <property name="text"> + <string>>> &Remove</string> + </property> + <property name="accel"> + <string>Alt+R</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Remove the selected file type from the project.</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>21</width> + <height>50</height> + </size> + </property> + </spacer> + </vbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox2</cstring> + </property> + <property name="title"> + <string>Available Types</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLineEdit"> + <property name="name"> + <cstring>m_pTypesEdit</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>You can enter custom file types here.</string> + </property> + </widget> + <widget class="QListBox"> + <item> + <property name="text"> + <string>*.c</string> + </property> + </item> + <item> + <property name="text"> + <string>*.h</string> + </property> + </item> + <item> + <property name="text"> + <string>*.l</string> + </property> + </item> + <item> + <property name="text"> + <string>*.y</string> + </property> + </item> + <item> + <property name="text"> + <string>*.S</string> + </property> + </item> + <item> + <property name="text"> + <string>*.cc</string> + </property> + </item> + <item> + <property name="text"> + <string>*.cpp</string> + </property> + </item> + <item> + <property name="text"> + <string>*.cxx</string> + </property> + </item> + <item> + <property name="text"> + <string>*.C</string> + </property> + </item> + <item> + <property name="text"> + <string>*.hh</string> + </property> + </item> + <item> + <property name="text"> + <string>*.hpp</string> + </property> + </item> + <item> + <property name="text"> + <string>*.hxx</string> + </property> + </item> + <item> + <property name="text"> + <string>*.H</string> + </property> + </item> + <property name="name"> + <cstring>m_pAvailTypesList</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>A list of standard file types.</string> + </property> + </widget> + </vbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer8_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>61</width> + <height>21</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>TabPage</cstring> + </property> + <attribute name="title"> + <string>&Options</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_pKernelCheck</cstring> + </property> + <property name="text"> + <string>Kernel project (-k)</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="whatsThis" stdset="0"> + <string>For kernel projects, symbols are not looked up in the standard include path.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_pInvCheck</cstring> + </property> + <property name="text"> + <string>Build inverted inde&x (-q)</string> + </property> + <property name="accel"> + <string>Alt+X</string> + </property> + <property name="whatsThis" stdset="0"> + <string>An inverted index may greatly speed up searches in a large project. The project's building process is longer, though.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_pNoCompCheck</cstring> + </property> + <property name="text"> + <string>Do not compress the database (-c)</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_pSlowPathCheck</cstring> + </property> + <property name="text"> + <string>Slower, but more accurate, function definition detection (-D)</string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout31</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_pAutoRebuildCheck</cstring> + </property> + <property name="text"> + <string>Refresh data&base automatically</string> + </property> + <property name="accel"> + <string>Alt+B</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Rebuild the database after changed files are saved to disk.</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer32</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>125</width> + <height>21</height> + </size> + </property> + </spacer> + <widget class="QLabel"> + <property name="name"> + <cstring>m_pAutoRebuildLabel</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>(Seconds)</string> + </property> + </widget> + <widget class="QSpinBox"> + <property name="name"> + <cstring>m_pAutoRebuildSpin</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="whatsThis" stdset="0"> + <string>Wait this number of seconds after the last save before rebuilding the database.</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout30</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_pACCheck</cstring> + </property> + <property name="text"> + <string>&Use symbol auto-completion</string> + </property> + <property name="accel"> + <string>Alt+U</string> + </property> + <property name="whatsThis" stdset="0"> + <string>As-you-type symbol completion.</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer33</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>61</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pACButton</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Options...</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout6</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_pTabWidthCheck</cstring> + </property> + <property name="text"> + <string>Override default tab width (Kate only)</string> + </property> + <property name="toolTip" stdset="0"> + <string>Overrides the editor's configured tab width</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer9</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>211</width> + <height>31</height> + </size> + </property> + </spacer> + <widget class="QSpinBox"> + <property name="name"> + <cstring>m_pTabWidthSpin</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + </hbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer31</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>21</width> + <height>40</height> + </size> + </property> + </spacer> + </vbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>TabPage</cstring> + </property> + <attribute name="title"> + <string>&Advanced</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_4</cstring> + </property> + <property name="text"> + <string>Ctags command line (Do not change unless you know what you are doing!)</string> + </property> + </widget> + <widget class="QTextEdit"> + <property name="name"> + <cstring>m_pCtagsCmdEdit</cstring> + </property> + <property name="textFormat"> + <enum>PlainText</enum> + </property> + <property name="autoFormatting"> + <set>AutoAll</set> + </property> + </widget> + </vbox> + </widget> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout19</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer8</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>141</width> + <height>21</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pCreateButton</cstring> + </property> + <property name="text"> + <string>&Create</string> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pCancelButton</cstring> + </property> + <property name="text"> + <string>Ca&ncel</string> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<connections> + <connection> + <sender>m_pAddButton</sender> + <signal>clicked()</signal> + <receiver>NewProjectLayout</receiver> + <slot>slotAddType()</slot> + </connection> + <connection> + <sender>m_pRemoveButton</sender> + <signal>clicked()</signal> + <receiver>NewProjectLayout</receiver> + <slot>slotRemoveType()</slot> + </connection> + <connection> + <sender>m_pAutoRebuildCheck</sender> + <signal>toggled(bool)</signal> + <receiver>m_pAutoRebuildSpin</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>m_pACCheck</sender> + <signal>toggled(bool)</signal> + <receiver>m_pACButton</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>m_pAvailTypesList</sender> + <signal>highlighted(const QString&)</signal> + <receiver>NewProjectLayout</receiver> + <slot>slotAvailTypesChanged(const QString&)</slot> + </connection> + <connection> + <sender>m_pTabWidthCheck</sender> + <signal>toggled(bool)</signal> + <receiver>m_pTabWidthSpin</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>m_pAutoRebuildCheck</sender> + <signal>toggled(bool)</signal> + <receiver>m_pAutoRebuildLabel</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>m_pHiddenDirCheck</sender> + <signal>toggled(bool)</signal> + <receiver>m_pPathRequester</receiver> + <slot>setDisabled(bool)</slot> + </connection> +</connections> +<tabstops> + <tabstop>m_pNameEdit</tabstop> + <tabstop>m_pPathRequester</tabstop> + <tabstop>m_pHiddenDirCheck</tabstop> + <tabstop>m_pSrcRootRequester</tabstop> + <tabstop>m_pAddButton</tabstop> + <tabstop>m_pRemoveButton</tabstop> + <tabstop>m_pKernelCheck</tabstop> + <tabstop>m_pInvCheck</tabstop> + <tabstop>m_pNoCompCheck</tabstop> + <tabstop>m_pSlowPathCheck</tabstop> + <tabstop>m_pAutoRebuildCheck</tabstop> + <tabstop>m_pAutoRebuildSpin</tabstop> + <tabstop>m_pACCheck</tabstop> + <tabstop>m_pACButton</tabstop> + <tabstop>m_pTabWidthCheck</tabstop> + <tabstop>m_pTabWidthSpin</tabstop> + <tabstop>m_pCreateButton</tabstop> + <tabstop>m_pCancelButton</tabstop> + <tabstop>tabWidget2</tabstop> + <tabstop>m_pTypesList</tabstop> + <tabstop>m_pTypesEdit</tabstop> + <tabstop>m_pAvailTypesList</tabstop> + <tabstop>m_pCtagsCmdEdit</tabstop> +</tabstops> +<slots> + <slot access="protected">slotAddType()</slot> + <slot access="protected">slotRemoveType()</slot> + <slot access="protected">slotAutoRebuildChanged(bool)</slot> + <slot access="protected">slotAutoCompletionChanged(bool)</slot> + <slot access="protected">slotAvailTypesChanged(const QString&)</slot> +</slots> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kurlrequester.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kurlrequester.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kpushbutton.h</includehint> +</includehints> +</UI> diff --git a/src/openprojectdlg.cpp b/src/openprojectdlg.cpp new file mode 100644 index 0000000..722fde6 --- /dev/null +++ b/src/openprojectdlg.cpp @@ -0,0 +1,131 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qpushbutton.h> +#include <qlineedit.h> +#include <qlistbox.h> +#include <kurlrequester.h> +#include "openprojectdlg.h" +#include "kscopeconfig.h" + +/** + * Class constructor. + * @param pParent The parent widget + * @param szName The widget's name + */ +OpenProjectDlg::OpenProjectDlg(QWidget* pParent, const char* szName) : + OpenProjectLayout(pParent, szName) +{ + loadRecent(); + m_pProjPathRequester->setFilter("cscope.proj"); +} + +/** + * Class destructor. + */ +OpenProjectDlg::~OpenProjectDlg() +{ +} + +/** + * @return The selected project path + */ +QString OpenProjectDlg::getPath() const +{ + return m_pProjPathRequester->url(); +} + +/** + * Sets the requester to reflect the selected project's directory, instead of + * the cscope.proj file. + * @param sProjPath The full path of the selected cscope.proj file + */ +void OpenProjectDlg::slotProjectSelected(const QString& sProjPath) +{ + QFileInfo fi(sProjPath); + m_pProjPathRequester->setURL(fi.dirPath(true)); +} + +/** + * Removes a project from the recent projects list. + * This slot is connected to the clicked() signal of the "Remove" button. + */ +void OpenProjectDlg::slotRemoveRecent() +{ + QListBoxItem* pItem; + + // Remove the selected item, if any + pItem = m_pRecentList->selectedItem(); + if (pItem != NULL) { + Config().removeRecentProject(pItem->text()); + m_pRecentList->removeItem(m_pRecentList->currentItem()); + } +} + +/** + * Selects a project for opening when an item is highlighted in the recent + * projects list. + * This slot is connected to the highlighted() signal of the recent projects + * list box. + * @param pItem The selected project item + */ +void OpenProjectDlg::slotSelectRecent(QListBoxItem* pItem) +{ + if (pItem != NULL) + m_pProjPathRequester->setURL(pItem->text()); +} + +/** + * Selects a project for opening and closes the dialogue when an item in the + * recent projects list is double-clicked. + * This slot is connected to the doubleClicked() signal of the recent + * projects list box. + * @param pItem The selected project item + */ +void OpenProjectDlg::slotOpenRecent(QListBoxItem* pItem) +{ + if (pItem != NULL) { + m_pProjPathRequester->setURL(pItem->text()); + accept(); + } +} + +/** + * Fills the recent projects list box with the project paths read from the + * configuration file. + */ +void OpenProjectDlg::loadRecent() +{ + const QStringList& slProjects = Config().getRecentProjects(); + QStringList::const_iterator itr; + + // Create a list item for each project in the list + for (itr = slProjects.begin(); itr != slProjects.end(); ++itr) + m_pRecentList->insertItem(*itr); +} + +#include "openprojectdlg.moc" diff --git a/src/openprojectdlg.h b/src/openprojectdlg.h new file mode 100644 index 0000000..cf492ad --- /dev/null +++ b/src/openprojectdlg.h @@ -0,0 +1,61 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef OPENPROJECTDLG_H +#define OPENPROJECTDLG_H + +#include <qwidget.h> +#include <openprojectlayout.h> + +/** + * A dialogue for selecting a project to open. + * Allows projects to be searched, and displays a list of previosuly loaded + * projects. + * @author Elad Lahav + */ + +class OpenProjectDlg : public OpenProjectLayout +{ + Q_OBJECT + +public: + OpenProjectDlg(QWidget* pParent = 0, const char* szName = 0); + ~OpenProjectDlg(); + + QString getPath() const; + +protected slots: + virtual void slotProjectSelected(const QString&); + virtual void slotRemoveRecent(); + virtual void slotSelectRecent(QListBoxItem*); + virtual void slotOpenRecent(QListBoxItem*); + +private: + void loadRecent(); +}; + +#endif diff --git a/src/openprojectlayout.ui b/src/openprojectlayout.ui new file mode 100644 index 0000000..88d52be --- /dev/null +++ b/src/openprojectlayout.ui @@ -0,0 +1,202 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>OpenProjectLayout</class> +<widget class="QDialog"> + <property name="name"> + <cstring>OpenProjectLayout</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>417</width> + <height>384</height> + </rect> + </property> + <property name="caption"> + <string>Open Project</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>buttonGroup5</cstring> + </property> + <property name="title"> + <string>Project Path</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KURLRequester"> + <property name="name"> + <cstring>m_pProjPathRequester</cstring> + </property> + </widget> + </hbox> + </widget> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>buttonGroup6</cstring> + </property> + <property name="title"> + <string>Recent Projects</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QListBox"> + <property name="name"> + <cstring>m_pRecentList</cstring> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout9</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>281</width> + <height>21</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pRemoveButton</cstring> + </property> + <property name="text"> + <string>Remove</string> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout9</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer6</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>201</width> + <height>31</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pOpenButton</cstring> + </property> + <property name="text"> + <string>&Open</string> + </property> + <property name="accel"> + <string>Alt+O</string> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pCancelButton</cstring> + </property> + <property name="text"> + <string>C&ancel</string> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<connections> + <connection> + <sender>m_pOpenButton</sender> + <signal>clicked()</signal> + <receiver>OpenProjectLayout</receiver> + <slot>accept()</slot> + </connection> + <connection> + <sender>m_pCancelButton</sender> + <signal>clicked()</signal> + <receiver>OpenProjectLayout</receiver> + <slot>reject()</slot> + </connection> + <connection> + <sender>m_pRemoveButton</sender> + <signal>clicked()</signal> + <receiver>OpenProjectLayout</receiver> + <slot>slotRemoveRecent()</slot> + </connection> + <connection> + <sender>m_pRecentList</sender> + <signal>highlighted(QListBoxItem*)</signal> + <receiver>OpenProjectLayout</receiver> + <slot>slotSelectRecent(QListBoxItem*)</slot> + </connection> + <connection> + <sender>m_pRecentList</sender> + <signal>doubleClicked(QListBoxItem*)</signal> + <receiver>OpenProjectLayout</receiver> + <slot>slotOpenRecent(QListBoxItem*)</slot> + </connection> + <connection> + <sender>m_pProjPathRequester</sender> + <signal>urlSelected(const QString&)</signal> + <receiver>OpenProjectLayout</receiver> + <slot>slotProjectSelected(const QString&)</slot> + </connection> + <connection> + <sender>m_pRecentList</sender> + <signal>returnPressed(QListBoxItem*)</signal> + <receiver>OpenProjectLayout</receiver> + <slot>slotOpenRecent(QListBoxItem*)</slot> + </connection> +</connections> +<slots> + <slot access="protected">slotRemoveRecent()</slot> + <slot access="protected">slotSelectRecent(QListBoxItem*)</slot> + <slot access="protected">slotOpenRecent(QListBoxItem*)</slot> + <slot access="protected">slotProjectSelected(const QString&)</slot> +</slots> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kurlrequester.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kpushbutton.h</includehint> +</includehints> +</UI> diff --git a/src/prefcolor.cpp b/src/prefcolor.cpp new file mode 100644 index 0000000..85beb4b --- /dev/null +++ b/src/prefcolor.cpp @@ -0,0 +1,172 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qlistview.h> +#include <qpainter.h> +#include <kcolordialog.h> +#include "prefcolor.h" +#include "kscopeconfig.h" + +/** + * A list view item that shows the name of a GUI element and the colour + * associated with it. + * The colour is presented in the form of a rectangle filled with that + * colour. + * @author Elad Lahav + */ +class ColorListItem : public QListViewItem +{ +public: + /** + * Class constructor. + * @param pList The owner list view + * @param ce The GUI element shown by this item + */ + ColorListItem(QListView* pList, KScopeConfig::ColorElement ce) : + QListViewItem(pList, Config().getColorName(ce), ""), + m_ce(ce) { + setColor(Config().getColor(ce)); + } + + /** + * @return The GUI element shown by this item + */ + KScopeConfig::ColorElement getElement() { return m_ce; } + + /** + * Changes the colour associated with this item. + * The function assigns a pixmap to the item which shows a rectangle + * filled with the requested colour. + * The colour set by this function is returned by getColor(). + * @param clr The colour to set + */ + void setColor(QColor clr) { + QPixmap pix; + QPainter painter; + int nWidth, nHeight; + + // Remember the colour + m_clr = clr; + + // Set the pixmap's size to fit into the list field + nWidth = listView()->columnWidth(1) - 1; + nHeight = height(); + pix.resize(nWidth, nHeight); + + // Draw on the pixmap + painter.begin(&pix); + painter.setBrush(clr); + painter.drawRect(0, 0, nWidth, nHeight); + painter.end(); + + // Set the pixmap to the item + setPixmap(1, pix); + } + + /** + * @return The colour associated with this item + */ + QColor getColor() { return m_clr; } + +private: + /** The GUI element shown by this item. */ + KScopeConfig::ColorElement m_ce; + + /** The colour associated with this item. */ + QColor m_clr; +}; + +/** + * Class constructor. + * @param pParent The parent widget + * @param szName The widget's name + */ +PrefColor::PrefColor(QWidget* pParent, const char* szName) : + PrefColorLayout(pParent, szName) +{ + m_pList->setColumnWidthMode(1, QListView::Manual); + + // Set initial values + load(); +} + +/** + * Class destructor. + */ +PrefColor::~PrefColor() +{ +} + +/** + * Reads the current settings from the configuration object, and applies them + * the the page's widget. + */ +void PrefColor::load() +{ + uint i; + ColorListItem* pItem; + + // Create a list item for every GUI element + for (i = 0; i <= KScopeConfig::LAST_COLOR; i++) + pItem = new ColorListItem(m_pList, (KScopeConfig::ColorElement)i); +} + +/** + * Commits settings changes to the configuration object. + */ +void PrefColor::apply() +{ + ColorListItem* pItem; + + // Create a list item for every GUI element + for (pItem = (ColorListItem*)m_pList->firstChild(); pItem != NULL; + pItem = (ColorListItem*)pItem->nextSibling()) { + Config().setColor(pItem->getElement(), pItem->getColor()); + } +} + +/** + * Displays a colour selection dialogue when an item is selected. + * If the user chooses a new colour, it is set to the selected item. + * This slot is connected to both the doubleClicked() and the returnPressed() + * signals of the list view. + * @param pItem The selected item + */ +void PrefColor::slotItemSelected(QListViewItem* pItem) +{ + ColorListItem* pClrItem; + QColor clr; + + pClrItem = (ColorListItem*)pItem; + if (KColorDialog::getColor(clr, pClrItem->getColor()) == + QDialog::Accepted) { + pClrItem->setColor(clr); + emit modified(); + } +} + +#include "prefcolor.moc" diff --git a/src/prefcolor.h b/src/prefcolor.h new file mode 100644 index 0000000..b26bed2 --- /dev/null +++ b/src/prefcolor.h @@ -0,0 +1,59 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef PREFCOLORDLG_H +#define PREFCOLORDLG_H + +#include <prefcolorlayout.h> + +/** + * A widget for selecting colours for KScope's main child-windows. + * @author Elad Lahav + */ +class PrefColor : public PrefColorLayout +{ + Q_OBJECT + +public: + PrefColor(QWidget* pParent = 0, const char* szName = 0); + ~PrefColor(); + + void load(); + void apply(); + +signals: + /** + * Emitted whenever the user makes a change to the dialogue's input + * widgets. + */ + void modified(); + +protected slots: + void slotItemSelected(QListViewItem*); +}; + +#endif diff --git a/src/prefcolorlayout.ui b/src/prefcolorlayout.ui new file mode 100644 index 0000000..8e15bda --- /dev/null +++ b/src/prefcolorlayout.ui @@ -0,0 +1,69 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>PrefColorLayout</class> +<widget class="QWidget"> + <property name="name"> + <cstring>PrefColorLayout</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>350</width> + <height>210</height> + </rect> + </property> + <property name="caption"> + <string>Form4</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QListView"> + <column> + <property name="text"> + <string>GUI Element</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Colour</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>false</bool> + </property> + </column> + <property name="name"> + <cstring>m_pList</cstring> + </property> + </widget> + </hbox> +</widget> +<connections> + <connection> + <sender>m_pList</sender> + <signal>doubleClicked(QListViewItem*)</signal> + <receiver>PrefColorLayout</receiver> + <slot>slotItemSelected(QListViewItem*)</slot> + </connection> + <connection> + <sender>m_pList</sender> + <signal>returnPressed(QListViewItem*)</signal> + <receiver>PrefColorLayout</receiver> + <slot>slotItemSelected(QListViewItem*)</slot> + </connection> +</connections> +<slots> + <slot access="protected">slotItemSelected(QListViewItem*)</slot> +</slots> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/src/preferencesdlg.cpp b/src/preferencesdlg.cpp new file mode 100644 index 0000000..ab13c33 --- /dev/null +++ b/src/preferencesdlg.cpp @@ -0,0 +1,206 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qlayout.h> +#include <klocale.h> +#include <kiconloader.h> +#include <kurlrequester.h> +#include <klineedit.h> +#include <qcheckbox.h> +#include <kcolorbutton.h> +#include <kmessagebox.h> +#include <kfontrequester.h> +#include "preferencesdlg.h" +#include "preffrontend.h" +#include "prefcolor.h" +#include "preffont.h" +#include "prefopt.h" +#include "kscopeconfig.h" +#include "cscopefrontend.h" +#include "ctagsfrontend.h" +#include "dotfrontend.h" + + +/** + * Class constructor. + * @param nPage The initial page to show + * @param pParent The parent widget + * @param szName The widget's name + */ +PreferencesDlg::PreferencesDlg(uint nPage, QWidget* pParent, + const char* szName) : + KDialogBase(IconList, i18n("Preferences"), Default | Ok | Apply | Cancel, + Ok, pParent, szName, 0) +{ + QFrame* pFrame; + QVBoxLayout* pLayout; + + // Create and add the "Frontend" page + pFrame = addPage(i18n("Programmes"), + i18n("Paths to back-end programmes"), + KGlobal::iconLoader()->loadIcon("run", KIcon::Panel, 0, false)); + pLayout = new QVBoxLayout(pFrame, 0, 0); + m_pPrefFrontend = new PrefFrontend(pFrame); + pLayout->addWidget(m_pPrefFrontend); + + // Create and add the "Colours" page + pFrame = addPage(i18n("Colours"), i18n("Window colours"), + KGlobal::iconLoader()->loadIcon("colors", KIcon::Panel, 0, false)); + pLayout = new QVBoxLayout(pFrame, 0, 0); + m_pPrefColor = new PrefColor(pFrame); + pLayout->addWidget(m_pPrefColor); + + // Create and add the "Fonts" page + pFrame = addPage(i18n("Fonts"), i18n("Window fonts"), + KGlobal::iconLoader()->loadIcon("fonts", KIcon::Panel, 0, false)); + pLayout = new QVBoxLayout(pFrame, 0, 0); + m_pPrefFont = new PrefFont(pFrame); + pLayout->addWidget(m_pPrefFont); + + // Create and add the "Options" page + pFrame = addPage(i18n("Options"), i18n("Misc. Options"), + KGlobal::iconLoader()->loadIcon("package_settings", + KIcon::Panel, 0, false)); + pLayout = new QVBoxLayout(pFrame, 0, 0); + m_pPrefOpt = new PrefOpt(pFrame); + pLayout->addWidget(m_pPrefOpt); + + // Make sure the "Apply" button is initially disabled + enableButtonApply(false); + + // Enable the "Apply" button when a parameter changes its value + connect(m_pPrefFrontend, SIGNAL(modified()), this, + SLOT(slotModified())); + connect(m_pPrefColor, SIGNAL(modified()), this, SLOT(slotModified())); + connect(m_pPrefFont, SIGNAL(modified()), this, SLOT(slotModified())); + connect(m_pPrefOpt, SIGNAL(modified()), this, SLOT(slotModified())); + + // Set the active page + showPage(nPage); +} + +/** + * Class destructor. + */ +PreferencesDlg::~PreferencesDlg() +{ +} + +/** + * Updates the dialog's widgets with the current configuration parameters. + */ +void PreferencesDlg::loadConfig() +{ + m_pPrefFrontend->load(); + m_pPrefColor->load(); + m_pPrefFont->load(); + m_pPrefOpt->load(); +} + +/** + * Sets the configured parameters to the global configuration object. + * This method is called when either the "OK" or the "Apply" button are + * clicked. Before the new settings are applied, their values are verified. + * @return true if the new parameters were applied successfully, false + * otherwise + */ +bool PreferencesDlg::updateConfig() +{ + // Verify configured paths lead to the executables + if (!verifyPaths()) + return false; + + // Apply the changes + m_pPrefFrontend->apply(); + m_pPrefColor->apply(); + m_pPrefFont->apply(); + m_pPrefOpt->apply(); + + emit applyPref(); + return true; +} + +/** + * Tests whether the paths set for Cscope and Ctags lead to executables. + * Cscope is verified by a different process. + */ +bool PreferencesDlg::verifyPaths() +{ + return (CtagsFrontend::verify(m_pPrefFrontend->m_pCtagsURL->url()) && + DotFrontend::verify(m_pPrefFrontend->m_pDotURL->url())); +} + +/** + * Updates the global configuration based on the values given in the + * preferences dialogue, and then closes the dialogue. + * This function is called after the user clicks the dialogue's "OK" button. + */ +void PreferencesDlg::accept() +{ + if (updateConfig()) + KDialogBase::accept(); +} + +/** + * Updates the global configuration based on the values given in the + * preferences dialogue, leaving the dialogue open. + * This function is called after the user clicks the dialogue's "Apply" + * button. + */ +void PreferencesDlg::slotApply() +{ + if (updateConfig()) + enableButtonApply(false); +} + +/** + * Resets all configuration parameters to their default values. + * This slot is called when the user clicks the "Default" button. + */ +void PreferencesDlg::slotDefault() +{ + // Prompt the user before applying default values + if (KMessageBox::questionYesNo(0, i18n("This would reset all your " + "configuration settings! Continue?")) == KMessageBox::Yes) { + // Load the default values + Config().loadDefault(); + loadConfig(); + + // Apply the default values + slotApply(); + } +} + +/** + * Enables the "Apply" button. + */ +void PreferencesDlg::slotModified() +{ + enableButtonApply(true); +} + +#include "preferencesdlg.moc" diff --git a/src/preferencesdlg.h b/src/preferencesdlg.h new file mode 100644 index 0000000..51fa2cb --- /dev/null +++ b/src/preferencesdlg.h @@ -0,0 +1,93 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef PREFERENCESDLG_H +#define PREFERENCESDLG_H + +#include <qwidget.h> +#include <kdialogbase.h> + +class PrefFrontend; +class PrefColor; +class PrefFont; +class PrefOpt; + +/** + * The main configuration dialog for KScope. + * This dialog displays a set of configuration pages, each dedicated to a + * different subject (frontend programme paths, colours, etc.) + * This code is based on a tutorial by Andreas Nicolai which can be found at + * http://www.kdevelop.org/doc/tutorial_settings + * @author Elad Lahav + */ + +class PreferencesDlg : public KDialogBase +{ + Q_OBJECT + +public: + PreferencesDlg(uint nPage = Frontend, QWidget* pParent = 0, const char* + szName = 0); + ~PreferencesDlg(); + + /** Available pages. */ + enum { Frontend = 0, Colors, Fonts, Options }; + +signals: + /** + * Emitted when the global configuration changes as a result of using + * this dialogue. + */ + void applyPref(); + +protected slots: + virtual void accept(); + virtual void slotApply(); + virtual void slotDefault(); + +private: + /** The front-end programmes page. */ + PrefFrontend* m_pPrefFrontend; + + /** The colours preference page. */ + PrefColor* m_pPrefColor; + + /** The fonts preference page. */ + PrefFont* m_pPrefFont; + + /** The additional options page. */ + PrefOpt* m_pPrefOpt; + + void loadConfig(); + bool updateConfig(); + bool verifyPaths(); + +private slots: + void slotModified(); +}; + +#endif diff --git a/src/preffont.cpp b/src/preffont.cpp new file mode 100644 index 0000000..288c699 --- /dev/null +++ b/src/preffont.cpp @@ -0,0 +1,174 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qlistview.h> +#include <qpainter.h> +#include <kfontdialog.h> +#include <klocale.h> +#include "preffont.h" +#include "kscopeconfig.h" + +/** + * A list view item that shows the name of a GUI element and the font + * associated with it. + * The font is presented in the form of a sample text drawn using this font. + * @author Elad Lahav + */ +class FontListItem : public QListViewItem +{ +public: + /** + * Class constructor. + * @param pList The owner list view + * @param fe The GUI element shown by this item + */ + FontListItem(QListView* pList, KScopeConfig::FontElement fe) : + QListViewItem(pList, Config().getFontName(fe), ""), + m_fe(fe) { + setFont(Config().getFont(fe)); + } + + /** + * @return The GUI element shown by this item + */ + KScopeConfig::FontElement getElement() { return m_fe; } + + /** + * Changes the font associated with this item. + * The function a sample text on a pixmap using this font, and then + * assigns the pixmap to the list item. + * The font set by this function is returned by getFont(). + * @param font The font to set + */ + void setFont(QFont font) { + QPixmap pix; + QFontMetrics fm(font); + QPainter painter; + QRect rc; + + // Remember the font + m_font = font; + + // Set the pixmap's size so it can contain the sample text + rc = fm.boundingRect(i18n("Sample")); + rc.moveTopLeft(QPoint(0, 0)); + pix.resize(rc.width(), rc.height()); + + // Draw on the pixmap + pix.fill(); + painter.begin(&pix); + painter.setFont(font); + painter.setPen(black); + painter.drawText(rc, Qt::AlignHCenter | Qt::AlignVCenter, + i18n("Sample")); + painter.end(); + + // Set the pixmap to the item + setPixmap(1, pix); + } + + /** + * @return The font associated with this item + */ + QFont getFont() { return m_font; } + +private: + /** The GUI element shown by this item. */ + KScopeConfig::FontElement m_fe; + + /** The font associated with this item. */ + QFont m_font; +}; + +/** + * Class constructor. + * @param pParent The parent widget + * @param szName The widget's name + */ +PrefFont::PrefFont(QWidget* pParent, const char* szName) : + PrefFontLayout(pParent, szName) +{ + // Set initial values + load(); +} + +/** + * Class destructor. + */ +PrefFont::~PrefFont() +{ +} + +/** + * Reads the current settings from the configuration object, and applies them + * the the page's widget. + */ +void PrefFont::load() +{ + uint i; + FontListItem* pItem; + + // Create a list item for every GUI element + for (i = 0; i <= KScopeConfig::LAST_FONT; i++) + pItem = new FontListItem(m_pList, (KScopeConfig::FontElement)i); +} + +/** + * Commits settings changes to the configuration object. + */ +void PrefFont::apply() +{ + FontListItem* pItem; + + // Create a list item for every GUI element + for (pItem = (FontListItem*)m_pList->firstChild(); pItem != NULL; + pItem = (FontListItem*)pItem->nextSibling()) { + Config().setFont(pItem->getElement(), pItem->getFont()); + } +} + +/** + * Displays a font selection dialogue when an item is selected. + * If the user chooses a new font, it is set to the selected item. + * This slot is connected to both the doubleClicked() and the returnPressed() + * signals of the list view. + * @param pItem The selected item + */ +void PrefFont::slotItemSelected(QListViewItem* pItem) +{ + FontListItem* pFontItem; + QFont font; + + pFontItem = (FontListItem*)pItem; + font = pFontItem->getFont(); + if (KFontDialog::getFont(font) == QDialog::Accepted) { + pFontItem->setFont(font); + emit modified(); + } +} + +#include "preffont.moc" diff --git a/src/preffont.h b/src/preffont.h new file mode 100644 index 0000000..66fdebc --- /dev/null +++ b/src/preffont.h @@ -0,0 +1,60 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef PREFFONT_H +#define PREFFONT_H + +#include "preffontlayout.h" + +/** + * A widget for selecting fonts for KScope's main child-windows. + * @author Elad Lahav + */ + +class PrefFont : public PrefFontLayout +{ + Q_OBJECT + +public: + PrefFont(QWidget* pParent = 0, const char* szName = 0); + ~PrefFont(); + + void load(); + void apply(); + +signals: + /** + * Emitted whenever the user makes a change to the dialogue's input + * widgets. + */ + void modified(); + +protected slots: + void slotItemSelected(QListViewItem*); +}; + +#endif diff --git a/src/preffontlayout.ui b/src/preffontlayout.ui new file mode 100644 index 0000000..983da52 --- /dev/null +++ b/src/preffontlayout.ui @@ -0,0 +1,69 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>PrefFontLayout</class> +<widget class="QWidget"> + <property name="name"> + <cstring>PrefFontLayout</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>363</width> + <height>210</height> + </rect> + </property> + <property name="caption"> + <string>Form4</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QListView"> + <column> + <property name="text"> + <string>GUI Element</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Font</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>false</bool> + </property> + </column> + <property name="name"> + <cstring>m_pList</cstring> + </property> + </widget> + </hbox> +</widget> +<connections> + <connection> + <sender>m_pList</sender> + <signal>doubleClicked(QListViewItem*)</signal> + <receiver>PrefFontLayout</receiver> + <slot>slotItemSelected(QListViewItem*)</slot> + </connection> + <connection> + <sender>m_pList</sender> + <signal>returnPressed(QListViewItem*)</signal> + <receiver>PrefFontLayout</receiver> + <slot>slotItemSelected(QListViewItem*)</slot> + </connection> +</connections> +<slots> + <slot access="protected">slotItemSelected(QListViewItem*)</slot> +</slots> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/src/preffrontend.cpp b/src/preffrontend.cpp new file mode 100644 index 0000000..3bedda7 --- /dev/null +++ b/src/preffrontend.cpp @@ -0,0 +1,238 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qcheckbox.h> +#include <qtextedit.h> +#include <kurlrequester.h> +#include <klineedit.h> +#include <kstandarddirs.h> +#include <klocale.h> +#include "preffrontend.h" +#include "kscopeconfig.h" +#include "configfrontend.h" + +/** + * Class constructor. + * @param pParent The parent widget + * @param szName The widget's name + */ +PrefFrontend::PrefFrontend(QWidget* pParent, const char* szName) : + PrefFrontendLayout(pParent, szName) +{ + // Set initial values + load(); + + // Attempt to guess paths based on the user's PATH environment variable + connect(m_pGuessButton, SIGNAL(clicked()), this, + SLOT(slotGuessPaths())); + + // Emit the modified() signal when a new path is set + connect(m_pCscopeURL, SIGNAL(textChanged(const QString&)), this, + SIGNAL(modified())); + connect(m_pCtagsURL, SIGNAL(textChanged(const QString&)), this, + SIGNAL(modified())); + connect(m_pDotURL, SIGNAL(textChanged(const QString&)), this, + SIGNAL(modified())); +} + +/** + * Class destructor. + */ +PrefFrontend::~PrefFrontend() +{ +} + +/** + * Reads the current settings from the configuration object, and applies them + * the the page's widget. + */ +void PrefFrontend::load() +{ + m_pCscopeURL->lineEdit()->setText(Config().getCscopePath()); + m_pCtagsURL->lineEdit()->setText(Config().getCtagsPath()); + m_pDotURL->lineEdit()->setText(Config().getDotPath()); +} + +/** + * Commits settings changes to the configuration object. + */ +void PrefFrontend::apply() +{ + Config().setCscopePath(m_pCscopeURL->url()); + Config().setCtagsPath(m_pCtagsURL->url()); + Config().setDotPath(m_pDotURL->url()); +} + +/** + * Emits the modified() signal whenever any of the paths edit widgets changes + * its contents. + * This slot is connected to the textChanged() signal of each of the path + * edit widgets. By emitting the modified() signal, the widget notifies the + * parent dialog it should enable the "Apply" button. + */ +void PrefFrontend::slotChanged(const QString&) +{ + emit modified(); +} + +/** + * Checks the user's PATH environment variable for a Cscope and Ctags + * executables. + * This is done by running the kscope_config script. + */ +void PrefFrontend::slotGuessPaths() +{ + ConfigFrontend* pConf; + + // Start with an empty results text widget + m_pScriptText->clear(); + + // Create a frontend object for the script + pConf = new ConfigFrontend(true); + + // Show tests and results in the text widget + connect(pConf, SIGNAL(test(uint)), this, + SLOT(slotAutoConfigTest(uint))); + connect(pConf, SIGNAL(result(uint, const QString&)), this, + SLOT(slotAutoConfigResult(uint, const QString&))); + + // Run the script + pConf->run(m_pCscopeURL->url(), m_pCtagsURL->url(), + m_pDotURL->url()); +} + +/** + * Shows the test being executed by the script in the text widget. + * This slot is connected to the test() signal of the ConfigFrontend object. + * @param nType The type of test being executed + */ +void PrefFrontend::slotAutoConfigTest(uint nType) +{ + switch (nType) { + case ConfigFrontend::CscopePath: + m_pScriptText->insert(i18n("Looking for Cscope...")); + break; + + case ConfigFrontend::CscopeVersion: + m_pScriptText->insert(i18n("Checking Cscope version...")); + break; + + case ConfigFrontend::CscopeVerbose: + m_pScriptText->insert(i18n("Cscope support for line mode verbose " + "output...")); + break; + + case ConfigFrontend::CscopeSlowPath: + m_pScriptText->insert(i18n("Cscope support slow path definitions... ")); + break; + + case ConfigFrontend::CtagsPath: + m_pScriptText->insert(i18n("Looking for Ctags...")); + break; + + case ConfigFrontend::CtagsExub: + m_pScriptText->insert(i18n("Ctags compatibilty with ctags-exuberant" + "...")); + break; + + case ConfigFrontend::DotPath: + m_pScriptText->insert(i18n("Looking for Dot...")); + break; + + case ConfigFrontend::DotPlain: + m_pScriptText->insert(i18n("Checking -Tplain...")); + break; + } +} + +/** + * Shows the result of a test executed by the configuration script, and + * adjusts the configuration widgets accordingly. + * @param nType The type of test that was executed + * @param sResult The test's result + */ +void PrefFrontend::slotAutoConfigResult(uint nType, const QString& sResult) +{ + QString sLine; + + sLine = sResult + "\n"; + + switch (nType) { + case ConfigFrontend::CscopePath: + m_pScriptText->insert(sLine); + if (sResult == "ERROR") + m_pCscopeURL->lineEdit()->setText(""); + else + m_pCscopeURL->lineEdit()->setText(sResult); + + break; + + case ConfigFrontend::CscopeVersion: + m_pScriptText->insert(sLine); + if (sResult == "ERROR") + m_pCscopeURL->lineEdit()->setText(""); + break; + + case ConfigFrontend::CscopeVerbose: + m_pScriptText->insert(sLine); + break; + + case ConfigFrontend::CscopeSlowPath: + m_pScriptText->insert(sLine); + break; + + case ConfigFrontend::CtagsPath: + m_pScriptText->insert(sLine); + if (sResult == "ERROR") + m_pCtagsURL->lineEdit()->setText(""); + else + m_pCtagsURL->lineEdit()->setText(sResult); + break; + + case ConfigFrontend::CtagsExub: + m_pScriptText->insert(sLine); + if (sResult == "ERROR") + m_pCtagsURL->lineEdit()->setText(""); + break; + + case ConfigFrontend::DotPath: + m_pScriptText->insert(sLine); + if (sResult == "ERROR") + m_pDotURL->lineEdit()->setText(""); + else + m_pDotURL->lineEdit()->setText(sResult); + break; + + case ConfigFrontend::DotPlain: + m_pScriptText->insert(sLine); + if (sResult == "ERROR") + m_pDotURL->lineEdit()->setText(""); + break; + } +} + +#include "preffrontend.moc" diff --git a/src/preffrontend.h b/src/preffrontend.h new file mode 100644 index 0000000..fb46242 --- /dev/null +++ b/src/preffrontend.h @@ -0,0 +1,65 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef PREFFRONTENDDLG_H +#define PREFFRONTENDDLG_H + +#include <qwidget.h> +#include <preffrontendlayout.h> + +/** + * A widget for setting the paths to various programmes to which KScope + * provides a front-end. + * @author Elad Lahav + */ + +class PrefFrontend : public PrefFrontendLayout +{ + Q_OBJECT + +public: + PrefFrontend(QWidget* pParent = 0, const char* szName = 0); + ~PrefFrontend(); + + void load(); + void apply(); + +signals: + /** + * Emitted whenever the user makes a change to the dialogue's input + * widgets. + */ + void modified(); + +private slots: + void slotChanged(const QString&); + void slotGuessPaths(); + void slotAutoConfigTest(uint); + void slotAutoConfigResult(uint, const QString&); +}; + +#endif diff --git a/src/preffrontendlayout.ui b/src/preffrontendlayout.ui new file mode 100644 index 0000000..e6b00c9 --- /dev/null +++ b/src/preffrontendlayout.ui @@ -0,0 +1,193 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>PrefFrontendLayout</class> +<widget class="QWidget"> + <property name="name"> + <cstring>PrefFrontendLayout</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>415</width> + <height>368</height> + </rect> + </property> + <property name="caption"> + <string>Form3</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout20</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout19</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Cscope path:</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Ctags path:</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>Dot path:</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout18</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KURLRequester"> + <property name="name"> + <cstring>m_pCscopeURL</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="KURLRequester"> + <property name="name"> + <cstring>m_pCtagsURL</cstring> + </property> + </widget> + <widget class="KURLRequester"> + <property name="name"> + <cstring>m_pDotURL</cstring> + </property> + </widget> + </vbox> + </widget> + </hbox> + </widget> + <widget class="Line"> + <property name="name"> + <cstring>line2</cstring> + </property> + <property name="frameShape"> + <enum>HLine</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout4</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer8</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>261</width> + <height>21</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pGuessButton</cstring> + </property> + <property name="text"> + <string>G&uess</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QTextEdit"> + <property name="name"> + <cstring>m_pScriptText</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<tabstops> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kurlrequester.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kurlrequester.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kurlrequester.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kpushbutton.h</includehint> +</includehints> +</UI> diff --git a/src/prefopt.cpp b/src/prefopt.cpp new file mode 100644 index 0000000..7b52d8f --- /dev/null +++ b/src/prefopt.cpp @@ -0,0 +1,145 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qcheckbox.h> +#include <qradiobutton.h> +#include <qlineedit.h> +#include <qlabel.h> +#include "prefopt.h" +#include "kscopeconfig.h" + +/** + * Class constructor. + * @param pParent The parent widget + * @param szName The widget's name + */ +PrefOpt::PrefOpt(QWidget* pParent, const char* szName) + : PrefOptLayout(pParent, szName) +{ + // Set initial values + load(); + + // Emit the "modified" signal whenever any of the widgets changes its + // its. This will notify the parent dialogue to enable its "Apply" + // button + connect(m_pReadOnlyCheck, SIGNAL(toggled(bool)), this, + SIGNAL(modified())); + connect(m_pLastProjCheck, SIGNAL(toggled(bool)), this, + SIGNAL(modified())); + connect(m_pTagHlCheck, SIGNAL(toggled(bool)), this, + SIGNAL(modified())); + connect(m_pBriefQueryCaptCheck, SIGNAL(toggled(bool)), this, + SIGNAL(modified())); + connect(m_pWarnModifiedOnDiskCheck, SIGNAL(toggled(bool)), this, + SIGNAL(modified())); + connect(m_pAutoSortCheck, SIGNAL(toggled(bool)), this, + SIGNAL(modified())); + connect(m_pExtEditorEdit, SIGNAL(textChanged(const QString&)), this, + SIGNAL(modified())); + connect(m_pSysProfileCB, SIGNAL(activated(int)), this, + SIGNAL(modified())); + connect(m_pEditorPopupCB, SIGNAL(activated(int)), this, + SIGNAL(modified())); +} + +/** + * Class destructor. + */ +PrefOpt::~PrefOpt() +{ +} + +/** + * Reads the current settings from the configuration object, and applies them + * the the page's widget. + */ +void PrefOpt::load() +{ + m_pReadOnlyCheck->setChecked(Config().getReadOnlyMode()); + m_pLastProjCheck->setChecked(Config().getLoadLastProj()); + m_pTagHlCheck->setChecked(Config().getAutoTagHl()); + m_pBriefQueryCaptCheck->setChecked(Config().getUseBriefQueryCaptions()); + m_pWarnModifiedOnDiskCheck->setChecked(Config().getWarnModifiedOnDisk()); + m_pAutoSortCheck->setChecked(Config().getAutoSortFiles()); + m_pExtEditorEdit->setText(Config().getExtEditor()); + + switch (Config().getSysProfile()) { + case KScopeConfig::Fast: + m_pSysProfileCB->setCurrentItem(0); + break; + + case KScopeConfig::Slow: + m_pSysProfileCB->setCurrentItem(1); + break; + } + + switch (Config().getEditorPopup()) { + case KScopeConfig::Embedded: + m_pEditorPopupCB->setCurrentItem(0); + break; + + case KScopeConfig::KScopeOnly: + m_pEditorPopupCB->setCurrentItem(1); + break; + } +} + +/** + * Commits settings changes to the configuration object. + */ +void PrefOpt::apply() +{ + Config().setReadOnlyMode(m_pReadOnlyCheck->isChecked()); + Config().setLoadLastProj(m_pLastProjCheck->isChecked()); + Config().setAutoTagHl(m_pTagHlCheck->isChecked()); + Config().setUseBriefQueryCaptions(m_pBriefQueryCaptCheck->isChecked()); + Config().setWarnModifiedOnDisk(m_pWarnModifiedOnDiskCheck->isChecked()); + Config().setAutoSortFiles(m_pAutoSortCheck->isChecked()); + Config().setExtEditor(m_pExtEditorEdit->text()); + + switch (m_pSysProfileCB->currentItem()) { + case 0 : + Config().setSysProfile(KScopeConfig::Fast); + break; + + case 1: + Config().setSysProfile(KScopeConfig::Slow); + break; + } + + switch (m_pEditorPopupCB->currentItem()) { + case 0: + Config().setEditorPopup(KScopeConfig::Embedded); + break; + + case 1: + Config().setEditorPopup(KScopeConfig::KScopeOnly); + break; + } +} + +#include "prefopt.moc" diff --git a/src/prefopt.h b/src/prefopt.h new file mode 100644 index 0000000..26e2572 --- /dev/null +++ b/src/prefopt.h @@ -0,0 +1,58 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef PREFOPT_H +#define PREFOPT_H + +#include "prefoptlayout.h" + +/** + * A widget for setting different global options. + * @author Elad Lahav + */ + +class PrefOpt : public PrefOptLayout +{ + Q_OBJECT + +public: + PrefOpt(QWidget* pParent = 0, const char* szName = 0); + ~PrefOpt(); + + void load(); + void apply(); + +signals: + /** + * Emitted whenever the user makes a change to the dialogue's input + * widgets. + */ + void modified(); +}; + +#endif + diff --git a/src/prefoptlayout.ui b/src/prefoptlayout.ui new file mode 100644 index 0000000..cbc8d07 --- /dev/null +++ b/src/prefoptlayout.ui @@ -0,0 +1,217 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>PrefOptLayout</class> +<widget class="QWidget"> + <property name="name"> + <cstring>PrefOptLayout</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>354</width> + <height>312</height> + </rect> + </property> + <property name="caption"> + <string>Form4</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Determines whether KScope should automatically load the last project when started.</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout7</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>m_pExtEditorLabel</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>External Editor</string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>m_pExtEditorEdit</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </hbox> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_pReadOnlyCheck</cstring> + </property> + <property name="text"> + <string>Read-Onl&y Mode</string> + </property> + <property name="accel"> + <string>Alt+Y</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Forces all editor windows to work in a read-only mode, so that the user cannot modify the displayed files.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_pLastProjCheck</cstring> + </property> + <property name="text"> + <string>Open Last Project on Start-Up</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_pTagHlCheck</cstring> + </property> + <property name="text"> + <string>Automatic Tag Highlighting</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Determines whether the tag list should highlight the relevant tag based on the cursor's position.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_pBriefQueryCaptCheck</cstring> + </property> + <property name="text"> + <string>Brief Tab Captions for &Query Pages</string> + </property> + <property name="whatsThis" stdset="0"> + <string>If set, the tab captions for query pages will be shortened, by using aliases for the query types.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_pWarnModifiedOnDiskCheck</cstring> + </property> + <property name="text"> + <string>Warn When a File is Modified Outside KScope</string> + </property> + <property name="whatsThis" stdset="0"> + <string>If set, the user is prompted whenever the currently edited file is changed by an external programme.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_pAutoSortCheck</cstring> + </property> + <property name="text"> + <string>Automatically Sort Files in the File List</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="whatsThis" stdset="0"> + <string>Sorts files in the project's file list when a project is loaded. This may be too slow for large projects on older machines.</string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout2</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>System Profile</string> + </property> + </widget> + <widget class="QComboBox"> + <item> + <property name="text"> + <string>Fast</string> + </property> + </item> + <item> + <property name="text"> + <string>Slow</string> + </property> + </item> + <property name="name"> + <cstring>m_pSysProfileCB</cstring> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout3</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Editor Popup Menu</string> + </property> + </widget> + <widget class="QComboBox"> + <item> + <property name="text"> + <string>Embedded</string> + </property> + </item> + <item> + <property name="text"> + <string>KScope Only</string> + </property> + </item> + <property name="name"> + <cstring>m_pEditorPopupCB</cstring> + </property> + </widget> + </hbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer11</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>21</width> + <height>20</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/src/progressdlg.cpp b/src/progressdlg.cpp new file mode 100644 index 0000000..418a3c9 --- /dev/null +++ b/src/progressdlg.cpp @@ -0,0 +1,116 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include "progressdlg.h" + +/** + * Class constructor. + * @param sCaption The dialogue's title + * @param sText The text to display + * @param pParent The parent widget + * @param szName The widget's name + */ +ProgressDlg::ProgressDlg(const QString& sCaption, const QString& sText, + QWidget* pParent, const char* szName) : + KProgressDialog(pParent, szName, sCaption, sText, true), + m_nIdleValue(-1) +{ + setAutoClose(false); + setAllowCancel(false); + + // Create the idle-progress timer + m_pIdleTimer = new QTimer(this); + + // Display a busy indicator by increasing the value of the idle counter + connect (m_pIdleTimer, SIGNAL(timeout()), this, SLOT(slotShowBusy())); +} + +/** + * Class destructor. + */ +ProgressDlg::~ProgressDlg() +{ +} + +/** + * Sets a new value to the progress bar. + * If the new value is non-zero, the progress bar is advanced. Otherwise, the + * idle timer is initiated to display a busy indicator. + * @param nValue The new value to set. + */ +void ProgressDlg::setValue(int nValue) +{ + KProgress* pProgress; + + pProgress = progressBar(); + + if (nValue != 0) { + // Do nothing if the value hasn't changed + if (nValue == pProgress->progress()) + return; + + // Handle first non-zero value + if (m_nIdleValue >= 0) { + m_pIdleTimer->stop(); + m_nIdleValue = -1; + pProgress->setPercentageVisible(true); + } + + // Set the new value + pProgress->setValue(nValue); + } + else if (m_nIdleValue == -1) { + // Handle first 0 value + pProgress->setValue(0); + pProgress->setPercentageVisible(false); + m_nIdleValue = 0; + m_pIdleTimer->start(200); + } +} + +void ProgressDlg::setIdle() +{ + m_nIdleValue = -1; + setValue(0); +} + +/** + * Increaes the value of the dummy counter by 1. + * This slot is called by the timeout() event of the idle timer. + */ +void ProgressDlg::slotShowBusy() +{ + // Increase the counter + m_nIdleValue += 5; + if (m_nIdleValue == 100) + m_nIdleValue = 0; + + // Set the value of the progress-bar + progressBar()->setValue(m_nIdleValue); +} + +#include "progressdlg.moc" diff --git a/src/progressdlg.h b/src/progressdlg.h new file mode 100644 index 0000000..d5f0e6b --- /dev/null +++ b/src/progressdlg.h @@ -0,0 +1,66 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef PROGRESSDLG_H +#define PROGRESSDLG_H + +#include <qwidget.h> +#include <qtimer.h> +#include <kprogress.h> + +/** + * An improved progress dialog. + * This variation of the standard KDE progress dialog displays a busy + * indicator while waiting for the first value greater than 0. + * @author Elad Lahav + */ + +class ProgressDlg : public KProgressDialog +{ + Q_OBJECT + +public: + ProgressDlg(const QString&, const QString&, QWidget* pParent = 0, const + char* szName = 0); + ~ProgressDlg(); + + void setValue(int); + void setIdle(); + +private: + /** When the value is 0, this timer initiates value changes that cause + the progress-bar to move. */ + QTimer* m_pIdleTimer; + + /** A dummy value used to move the progress-bar while the value is 0. */ + int m_nIdleValue; + +private slots: + void slotShowBusy(); +}; + +#endif diff --git a/src/project.cpp b/src/project.cpp new file mode 100644 index 0000000..06e1332 --- /dev/null +++ b/src/project.cpp @@ -0,0 +1,442 @@ +/*************************************************************************** + * + * Copyright (C) 2007 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <kmessagebox.h> +#include <klocale.h> +#include "project.h" +#include "kscopeconfig.h" +#include "cscopefrontend.h" + +#define PROJECT_CONFIG_VER 2 + +inline void flListFromStringList(FileLocationList& fll, const QStringList& sl) +{ + QStringList::ConstIterator itr; + QString sPath; + uint nLine, nCol; + + // Transform the string into a list of file locations + for (itr = sl.begin(); itr != sl.end(); ++itr) { + sPath = (*itr).section(':', 0, 0); + nLine = (*itr).section(':', 1, 1).toUInt(); + nCol = (*itr).section(':', 2, 2).toUInt(); + fll.append(new FileLocation(sPath, nLine, nCol)); + } +} + +inline void stringListFromFlList(QStringList& sl, const FileLocationList& fll) +{ + FileLocationList* pList; + FileLocation* pLoc; + QString sLoc; + + // Nasty... + pList = (FileLocationList*)&fll; + sl.clear(); + + // Turn the object list into a string list, so that it can be written in + // the configuration file + for (pLoc = pList->first(); pLoc != NULL; pLoc = pList->next()) { + sLoc = ""; + QTextOStream(&sLoc) << pLoc->m_sPath << ":" << pLoc->m_nLine << ":" + << pLoc->m_nCol; + sl.append(sLoc); + } +} + +/** + */ +Project::Project() : ProjectBase(), + m_pConf(NULL) +{ +} + +/** + */ +Project::~Project() +{ + close(); +} + +/** + */ +bool Project::open(const QString& sPath) +{ + QString sConfFile; + Options opt; + + // Associate the object with the project directory + m_dir.setPath(sPath); + if (!m_dir.exists()) { + KMessageBox::error(0, i18n("Project directory does not exist")); + return false; + } + + // Initialise the file-list file object + m_fiFileList.setName(sPath + "/cscope.files"); + + // Open the configuration files + m_pConf = new KConfig(sPath + "/cscope.proj"); + + // Verify the configuration file's version is compatible + m_pConf->setGroup(""); + if (m_pConf->readUnsignedNumEntry("Version", 0) != PROJECT_CONFIG_VER) { + KMessageBox::error(0, i18n("Your project is not compatible with this " + "version of KScope.\nPlease re-create the project.")); + return false; + } + + // Get the project name + m_pConf->setGroup("Project"); + m_sName = m_pConf->readEntry("Name"); + if (m_sName == QString::null) { + KMessageBox::error(0, i18n("Cannot read project name")); + return false; + } + + // Get stored options + initOptions(); + + // Set default make values for new projects (overriden in loadSession(), + // which is not called for new projects) + m_sMakeRoot = getSourceRoot(); + m_sMakeCmd = "make"; + + return true; +} + +/** + */ +void Project::close() +{ + if (m_pConf) + delete m_pConf; + + m_fiFileList.close(); +} + +/** + * Returns a semi-colon separated list of the file types included in the + * current project. + */ +QString Project::getFileTypes() const +{ + QString sTypes; + + m_pConf->setGroup("Project"); + return m_pConf->readEntry("FileTypes"); +} + +/** + * Reads the project's options from the configuration file. + * @param opt A structure to fill with the read options + */ +void Project::getOptions(Options& opt) const +{ + // Get project properties + m_pConf->setGroup("Project"); + opt.sSrcRootPath = m_pConf->readEntry("RootPath", "/"); + opt.slFileTypes = m_pConf->readListEntry("FileTypes", ' '); + opt.bKernel = m_pConf->readBoolEntry("Kernel", DEF_IS_KERNEL); + opt.bInvIndex = m_pConf->readBoolEntry("InvIndex", DEF_INV_INDEX); + opt.bNoCompress = m_pConf->readBoolEntry("NoCompress", DEF_NO_COMPRESS); + opt.bSlowPathDef = m_pConf->readBoolEntry("SlowPathDef", DEF_SLOW_PATH); + opt.nAutoRebuildTime = m_pConf->readNumEntry("AutoRebuildTime"); + opt.nTabWidth = m_pConf->readUnsignedNumEntry("TabWidth"); + opt.sCtagsCmd = m_pConf->readEntry("CtagsCommand", DEF_CTAGS_COMMAND); + + // Get auto-completion options + m_pConf->setGroup("AutoCompletion"); + opt.bACEnabled = m_pConf->readBoolEntry("Enabled"); + opt.nACMinChars = m_pConf->readUnsignedNumEntry("MinChars", + DEF_AC_MIN_CHARS); + opt.nACDelay = m_pConf->readUnsignedNumEntry("Delay", DEF_AC_DELAY); + opt.nACMaxEntries = m_pConf->readUnsignedNumEntry("MaxEntries", + DEF_AC_MAX_ENTRIES); +} + +/** + * Sets project options. + * @param opt A structure containing the new parameters to set + */ +void Project::setOptions(const Options& opt) +{ + // Write the options to the configuration nfile + writeOptions(m_pConf, opt); + + // Update project parameters + initOptions(); +} + +/** + */ +void Project::loadSession(Session& sess) +{ + QStringList slEntry; + + m_pConf->setGroup("Session"); + + // Read the list of open file locations + slEntry = m_pConf->readListEntry("OpenFiles"); + flListFromStringList(sess.fllOpenFiles, slEntry); + + // Get the path of the last viewed file + sess.sLastFile = m_pConf->readEntry("LastOpenFile"); + + // Read the lists of locked query files and call-tree/graph files + sess.slQueryFiles = m_pConf->readListEntry("QueryFiles"); + sess.slCallTreeFiles = m_pConf->readListEntry("CallTreeFiles"); + + // Read the list of bookmarks + slEntry = m_pConf->readListEntry("Bookmarks"); + flListFromStringList(sess.fllBookmarks, slEntry); + + // Read make-related information + sess.sMakeCmd = m_pConf->readEntry("MakeCommand", "make"); + sess.sMakeRoot = m_pConf->readEntry("MakeRoot", getSourceRoot()); + + // Cache make values + m_sMakeCmd = sess.sMakeCmd; + m_sMakeRoot = sess.sMakeRoot; +} + +/** + * Saves session-related information in the project's configuration file. + * @param sess Session parameters + */ +void Project::storeSession(const Session& sess) +{ + QStringList slEntry; + + m_pConf->setGroup("Session"); + + // Write the list of open file locations + stringListFromFlList(slEntry, sess.fllOpenFiles); + m_pConf->writeEntry("OpenFiles", slEntry); + + // Write the path of the last viewed file + m_pConf->writeEntry("LastOpenFile", sess.sLastFile); + + // Write the lists of locked query files and call-tree/graph files + m_pConf->writeEntry("QueryFiles", sess.slQueryFiles); + m_pConf->writeEntry("CallTreeFiles", sess.slCallTreeFiles); + + // Write the list of bookmarks + stringListFromFlList(slEntry, sess.fllBookmarks); + m_pConf->writeEntry("Bookmarks", slEntry); + + // Write make-related information + // Be careful not to write empty strings, as they may occur if the make + // dialogue was not invoked during this session + if (!sess.sMakeCmd.isEmpty()) + m_pConf->writeEntry("MakeCommand", sess.sMakeCmd); + if (!sess.sMakeRoot.isEmpty()) + m_pConf->writeEntry("MakeRoot", sess.sMakeRoot); +} + +/** + * Fills a list object with all files in the project. + * List items are created by reading and parsing all file name entries from + * the project's 'cscope.files' file. + * Note that the file may contain option lines, beginning with a dash. These + * should be ignored. + * @param pList Pointer to the object to fill + */ +bool Project::loadFileList(FileListTarget* pList) +{ + QString sFilePath; + + // Open the 'cscope.files' file + if (!m_fiFileList.open(IO_ReadOnly)) + return false; + + // Read all file names from the file + QTextStream str(&m_fiFileList); + while ((sFilePath = str.readLine()) != QString::null) { + // Skip option lines + if (sFilePath.at(0) == '-') + continue; + + // Set the new list item + pList->addItem(sFilePath); + } + + m_fiFileList.close(); + return true; +} + +/** + * Writes all file entries in a list view widget to the project's + * 'cscope.files' file (replacing current file contents.) + * @param pList Pointer to the object from which to take the new entries + */ +bool Project::storeFileList(FileListSource* pList) +{ + QString sFilePath; + + // Open the 'cscope.files' file + if (!m_fiFileList.open(IO_WriteOnly | IO_Truncate)) + return false; + + QTextStream str(&m_fiFileList); + + // Write all file names + if (pList->firstItem(sFilePath)) { + do { + str << sFilePath << "\n"; + } while (pList->nextItem(sFilePath)); + } + + m_fiFileList.close(); + return true; +} + +/** + * Adds a single file to the file list. + * @param sPath The path of the file to add + * @return true if successful, false otherwise + */ +bool Project::addFile(const QString& sPath) +{ + // Open the 'cscope.files' file + if (!m_fiFileList.open(IO_WriteOnly | IO_Append)) + return false; + + // Write the file path + QTextStream str(&m_fiFileList); + str << sPath << "\n"; + + m_fiFileList.close(); + return true; +} + +/** + * Determines whether the project includes any files. + * Reads the 'cscope.files' file and looks for any file names in it. If none + * is present, then the project is considered empty. + * Note that the file may contain option lines, beginning with a dash. These + * should be ignored. + * @return true if no files are included in the project, false otherwise + */ +bool Project::isEmpty() +{ + QString sPath, sFileName; + bool bResult = true; + + // Open the 'cscope.files' file + if (!m_fiFileList.open(IO_ReadOnly)) + return true; + + // Find at least one file name entry in the file + QTextStream str(&m_fiFileList); + while ((sPath = str.readLine()) != QString::null) { + if (sPath.at(0) != '-') { + bResult = false; + break; + } + } + + m_fiFileList.close(); + return bResult; +} + +/** + * Copies the list of previously queried symbols to the target object. + * @param slSymHistory The list object to copy into + */ +void Project::getSymHistory(QStringList& slSymHistory) const +{ + slSymHistory = m_slSymHistory; +} + +/** + * Copies the list of previously queried symbols from the target object. + * @param slSymHistory The list object to copy from + */ +void Project::setSymHistory(QStringList& slSymHistory) +{ + m_slSymHistory = slSymHistory; +} + +void Project::getMakeParams(QString& sCmd, QString& sDir) const +{ + sCmd = m_sMakeCmd; + sDir = m_sMakeRoot; +} + +/** + * Creates a project by writing a configuration file inside the given + * directory. + * @param sName The project's name + * @param sPath The full path of the project's directory + * @param opt Project options + */ +bool Project::create(const QString& sName, const QString& sPath, + const Options& opt) +{ + // Prepare the project's files + KConfig conf(sPath + "/cscope.proj"); + + // Write the configuration file version + conf.setGroup(""); + conf.writeEntry("Version", PROJECT_CONFIG_VER); + + // Write project properties in the configuration file + conf.setGroup("Project"); + conf.writeEntry("Name", sName); + writeOptions(&conf, opt); + + // Flush the config file data, so the project is created even if KScope + // crashes... + conf.sync(); + + return true; +} + +void Project::writeOptions(KConfig* pConf, const Options& opt) +{ + pConf->setGroup("Project"); + pConf->writeEntry("RootPath", opt.sSrcRootPath); + pConf->writeEntry("FileTypes", opt.slFileTypes.join(" ")); + pConf->writeEntry("Kernel", opt.bKernel); + pConf->writeEntry("InvIndex", opt.bInvIndex); + pConf->writeEntry("NoCompress", opt.bNoCompress); + pConf->writeEntry("SlowPathDef", opt.bSlowPathDef); + pConf->writeEntry("AutoRebuildTime", opt.nAutoRebuildTime); + pConf->writeEntry("TabWidth", opt.nTabWidth); + pConf->writeEntry("CtagsCommand", opt.sCtagsCmd); + + // Set auto-completion options + pConf->setGroup("AutoCompletion"); + pConf->writeEntry("Enabled", opt.bACEnabled); + pConf->writeEntry("MinChars", opt.nACMinChars); + pConf->writeEntry("Delay", opt.nACDelay); + pConf->writeEntry("MaxEntries", opt.nACMaxEntries); +} diff --git a/src/project.h b/src/project.h new file mode 100644 index 0000000..a2679b2 --- /dev/null +++ b/src/project.h @@ -0,0 +1,92 @@ +/*************************************************************************** + * + * Copyright (C) 2007 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef PROJECT_H +#define PROJECT_H + +#include <projectbase.h> + +/** + * @author Elad Lahav + */ +class Project : public ProjectBase +{ +public: + Project(); + virtual ~Project(); + + struct Session { + FileLocationList fllOpenFiles; + QString sLastFile; + QStringList slQueryFiles; + QStringList slCallTreeFiles; + FileLocationList fllBookmarks; + QString sMakeCmd; + QString sMakeRoot; + }; + + virtual bool open(const QString&); + virtual bool loadFileList(FileListTarget*); + virtual bool storeFileList(FileListSource*); + virtual bool addFile(const QString&); + virtual bool isEmpty(); + virtual void close(); + + virtual QString getFileTypes() const; + virtual void getOptions(Options&) const; + virtual void setOptions(const Options&); + virtual void loadSession(Session&); + virtual void storeSession(const Session&); + virtual void getSymHistory(QStringList&) const; + virtual void setSymHistory(QStringList&); + virtual void getMakeParams(QString&, QString&) const; + + /** + * Determines whether a project is based on a Cscope.out file, and is + * therefore considered as a temporary project. + * @return true if this is a temporary project, false otherwise + */ + virtual bool isTemporary() { return false; } + + static bool create(const QString&, const QString&, const Options&); + +private: + /** The configuration file ("cscope.proj") */ + KConfig* m_pConf; + + /** The file that holds the paths of all source files in this project + ("cscope.files") */ + QFile m_fiFileList; + + QString m_sMakeCmd; + + QString m_sMakeRoot; + + static void writeOptions(KConfig*, const Options&); +}; + +#endif diff --git a/src/projectbase.cpp b/src/projectbase.cpp new file mode 100644 index 0000000..f99c045 --- /dev/null +++ b/src/projectbase.cpp @@ -0,0 +1,190 @@ +/*************************************************************************** + * + * Copyright (C) 2007 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include "projectbase.h" +#include "kscopeconfig.h" +#include "cscopefrontend.h" + +ProjectBase::ProjectBase() +{ +} + +ProjectBase::~ProjectBase() +{ +} + +bool ProjectBase::open(const QString& sPath) +{ + QFileInfo fi(sPath); + + // Make sure the file exists, and that is is a cross-reference file + if (!fi.exists() || !isCscopeOut(fi.absFilePath())) + return false; + + // Set the project's directory + m_dir = fi.dirPath(true); + + // Set the name of the project to be the full path of the file + m_sName = fi.absFilePath(); + + // Initialise project options (assume source root is the folder holding the + // cscope.out file) + getDefOptions(m_opt); + m_opt.sSrcRootPath = m_dir.absPath(); + + return true; +} + +/** + * Determines if the cscope.out file for this project exists. + * @return true if the database exists, false otherwise + */ +bool ProjectBase::dbExists() +{ + return m_dir.exists("cscope.out"); +} + +void ProjectBase::getOptions(Options& opt) const +{ + getDefOptions(opt); +} + +void ProjectBase::getMakeParams(QString& sCmd, QString& sDir) const +{ + sCmd = "make"; + sDir = getSourceRoot(); +} + +/** + * Fills a structure with default properties for a new project. + * Default properties are partly based on the system profile. + * @param opt The structure to fill + */ +void ProjectBase::getDefOptions(Options& opt) +{ + // Set default source path to file-system root + opt.sSrcRootPath = "/"; + + // Initialise MIME-type list + opt.slFileTypes.append("*.c"); + opt.slFileTypes.append("*.h"); + + // Set other options + opt.bKernel = DEF_IS_KERNEL; + opt.bInvIndex = DEF_INV_INDEX; + opt.bNoCompress = DEF_NO_COMPRESS; + opt.bSlowPathDef = DEF_SLOW_PATH; + opt.nACMinChars = DEF_AC_MIN_CHARS; + opt.nACDelay = DEF_AC_DELAY; + opt.nACMaxEntries = DEF_AC_MAX_ENTRIES; + opt.nTabWidth = DEF_TAB_WIDTH; + + // Set profile-dependant options + if (Config().getSysProfile() == KScopeConfig::Fast) { + opt.nAutoRebuildTime = 10; + opt.bACEnabled = true; + } + else { + opt.nAutoRebuildTime = -1; + opt.bACEnabled = false; + } +} + +void ProjectBase::initOptions() +{ + // Load the options + getOptions(m_opt); + + // Create the argument list for invoking Cscope + m_nArgs = 0; + if (m_opt.bKernel) + m_nArgs |= CscopeFrontend::Kernel; + if (m_opt.bInvIndex) + m_nArgs |= CscopeFrontend::InvIndex; + if (m_opt.bNoCompress) + m_nArgs |= CscopeFrontend::NoCompression; + if (m_opt.bSlowPathDef) + m_nArgs |= CscopeFrontend::SlowPathDef; +} + +/** + * Determines if the given file is a Cscope cross-reference database. + * @param sPath The full path of the file to check + * @return true if the given file is a cscope.out file, false otherwise + */ +bool ProjectBase::isCscopeOut(const QString& sPath) +{ + QFile file(sPath); + QString sLine; + int nVer; + char szDir[PATH_MAX]; + + // Try to open the file + if (!file.open(IO_ReadOnly)) + return false; + + // Check if the first line matches the expected format + sLine = QTextStream(&file).readLine(); + return sscanf(sLine.latin1(), "cscope %d %s", &nVer, szDir) == 2; +} + +/** + * Fills a list object with all files in the project. + * List items are created by reading and parsing all file name entries from + * the project's 'cscope.files' file. + * Note that the file may contain option lines, beginning with a dash. These + * should be ignored. + * @param pList Pointer to the object to fill + */ +bool ProjectBase::loadFileList(FileListTarget* pList) +{ + QString sFilePath; + QFile file; + + // Make sure the file exists + if (!m_dir.exists("cscope.files")) + return false; + + // Open the file + file.setName(m_dir.absPath() + "/cscope.files"); + if (!file.open(IO_ReadOnly)) + return false; + + // Read all file names from the file + QTextStream str(&file); + while ((sFilePath = str.readLine()) != QString::null) { + // Skip option lines + if (sFilePath.at(0) == '-') + continue; + + // Set the new list item + pList->addItem(sFilePath); + } + + file.close(); + return true; +} diff --git a/src/projectbase.h b/src/projectbase.h new file mode 100644 index 0000000..4170652 --- /dev/null +++ b/src/projectbase.h @@ -0,0 +1,281 @@ +/*************************************************************************** + * + * Copyright (C) 2007 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef PROJECTBASE_H +#define PROJECTBASE_H + +#include <qstringlist.h> +#include <qdir.h> +#include <qfile.h> +#include <kconfig.h> + +#define DEF_IS_KERNEL false +#define DEF_INV_INDEX true +#define DEF_NO_COMPRESS false +#define DEF_SLOW_PATH false +#define DEF_AC_MIN_CHARS 3 +#define DEF_AC_DELAY 500 +#define DEF_AC_MAX_ENTRIES 100 +#define DEF_TAB_WIDTH 0 /* Use editor's default */ +#define DEF_CTAGS_COMMAND \ + "--regex-c=\"/^[ \\t]*([_a-zA-Z][_a-zA-Z0-9]*):/\\1/l,label/\" " \ + "--regex-c=\"/^[ \\t]*#[ \\t]*include[ \\t]*[\\\"<]" \ + "([_a-zA-Z0-9\\.\\/]*)[\\\">]/\\1/i,include/\" " \ + "--regex-c++=\"/^[ \\t]*#[ \\t]*include[ \\t]*[\\\"<]" \ + "([_a-zA-Z0-9\\.\\/]*)[\\\">]/\\1/i,include/\"" + +/** + * Abstract base class for classes that need the list of project files. + * Objects of classes derived from this one are used as a parameter to + * ProjectManager::fillList(), which reads all file entries in the project, + * and calls addItem() for each. + * Any class that wishes to retrieve the project's file list, should inherit + * from this class, and implement addItem(). + * @author Elad Lahav + */ + +class FileListTarget +{ +public: + /** + * Class constructor. + */ + FileListTarget() {} + + /** + * Class destructor. + */ + virtual ~FileListTarget() {} + + /** + * Appends a file to the list. + * @param sFilePath The full path of the file to add + */ + virtual void addItem(const QString& sFilePath) = 0; +}; + +/** + * Abstract base class for classes that need the list of project files. + * Objects of classes derived from this one are used as a parameter to + * ProjectManager::writeList(), which calls getFirstItem() and getNextItem(), + * and writes the returned values to the project's 'cscope.files' file. + * Any class that wishes to retrieve the project's file list, should inherit + * from this class, and implement firstItem() and nextItem(). + * @author Elad Lahav + */ + +class FileListSource +{ +public: + /** + * Class constructor. + */ + FileListSource() {} + + /** + * Class destructor. + */ + virtual ~FileListSource() {} + + /** + * Returns the first file in the list, and initiates a new iteration. + * @param sFilePath Holds the path of the first file, upon return + * @return true if there are more files, false otherwise + */ + virtual bool firstItem(QString& sFilePath) = 0; + + /** + * Returns the next file in the list. + * @param sFilePath Holds the path of the file, upon return + * @return true if there are more files, false otherwise + */ + virtual bool nextItem(QString& sFilePath) = 0; +}; + +/** + * Defines a cursor location inside a file. + * This structure is used to store project session information. + * @author Elad Lahav + */ +struct FileLocation +{ + /** + * Struct constructor. + * @param sPath The full path of the file + * @param nLine The line position of the cursor + * @param nCol The column position of the cursor + */ + FileLocation(QString sPath, uint nLine, uint nCol) : m_sPath(sPath), + m_nLine(nLine), m_nCol(nCol) {} + + /** The full path of the file. */ + QString m_sPath; + + /** The line position of the cursor. */ + uint m_nLine; + + /** The column position of the cursor. */ + uint m_nCol; +}; + +/** + * A list of file locations used for restoring a session. + */ +typedef QPtrList<FileLocation> FileLocationList; + +class FileSemaphore; + +/** + * @author Elad Lahav + */ +class ProjectBase +{ +public: + ProjectBase(); + virtual ~ProjectBase(); + + /** + * Configurable project options. + */ + struct Options { + QString sSrcRootPath; + + /** A list of MIME-types that determines which files are included in + the project. */ + QStringList slFileTypes; + + /** true if the -k option for CScope should be used. */ + bool bKernel; + + /** true if Cscope should build an inverted index. */ + bool bInvIndex; + + /** true if the -c option for CScope should be used. */ + bool bNoCompress; + + /** true if the -D option for CScope should be used. */ + bool bSlowPathDef; + + /** The time, in milliseconds, after which the database should be + automatically rebuilt (-1 if this option is disabled). */ + int nAutoRebuildTime; + + /** true to use auto-completion. */ + bool bACEnabled; + + /** Minimum number of characters in a symbol for auto-completion. */ + uint nACMinChars; + + /** Time interval, in milliseconds, before auto-completion is + started. */ + uint nACDelay; + + /** Maximal number of entries for auto-completion. */ + uint nACMaxEntries; + + /** Per-project tab width (overrides editor settings). */ + uint nTabWidth; + + /** Ctags command line. */ + QString sCtagsCmd; + }; + + virtual bool open(const QString&); + virtual bool loadFileList(FileListTarget*); + virtual bool storeFileList(FileListSource*) { return false; } + virtual bool isEmpty() { return false; } + bool dbExists(); + virtual void close() {} + + virtual QString getFileTypes() const { return QString::null; } + virtual void getOptions(Options&) const; + virtual void setOptions(const Options&) {} + virtual void getSymHistory(QStringList&) const {} + virtual void setSymHistory(QStringList&) {} + virtual void getMakeParams(QString&, QString&) const; + + /** + * Determines whether a project is based on a Cscope.out file, and is + * therefore considered as a temporary project. + * @return true if this is a temporary project, false otherwise + */ + virtual bool isTemporary() { return true; } + + /** + * @return The name of the current project + */ + QString getName() const { return m_sName; } + + /** + * @return The full path of the project's directory + */ + QString getPath() const { return m_dir.absPath(); } + + /** + * @return Command-line arguments to pass to a Cscope object, based on + * project's options + */ + uint getArgs() const { return m_nArgs; } + + const QString& getSourceRoot() const { return m_opt.sSrcRootPath; } + + /** + * @return The time, in seconds, to wait before rebuilding the + * cross-refernce database. + */ + int getAutoRebuildTime() const { return m_opt.nAutoRebuildTime; } + + /** + * @return The tab width to use (0 to use the editor's default) + */ + uint getTabWidth() const { return m_opt.nTabWidth; } + + static void getDefOptions(Options&); + +protected: + /** The name of the project, as written in the configuration file */ + QString m_sName; + + /** The directory associated with the project */ + QDir m_dir; + + /** A cached version of the project's options. */ + Options m_opt; + + /** A list of Cscope command-line arguments based on the project's + options. */ + uint m_nArgs; + + /** A list of symbols previously queried. */ + QStringList m_slSymHistory; + + void initOptions(); + + static bool isCscopeOut(const QString&); +}; + +#endif diff --git a/src/projectfilesdlg.cpp b/src/projectfilesdlg.cpp new file mode 100644 index 0000000..de84417 --- /dev/null +++ b/src/projectfilesdlg.cpp @@ -0,0 +1,439 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qpushbutton.h> +#include <qlistview.h> +#include <qlineedit.h> +#include <qregexp.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kfiledialog.h> +#include "projectfilesdlg.h" +#include "dirscanner.h" +#include "scanprogressdlg.h" +#include "kscopeconfig.h" + +/** + * Class constructor. + * @param pProjMgr Pointer the KScope's project manager object + * @param pParent The parent widget + * @param szName The widget's name + */ +ProjectFilesDlg::ProjectFilesDlg(Project* pProj, QWidget* pParent, + const char* szName) : + ProjectFilesLayout(pParent, szName), + m_pProj(pProj), + m_pScanDlg(NULL), + m_pItrItem(NULL), + m_pLastItem(NULL) +{ + // Create the scanner object + m_pScanner = new DirScanner(this, &m_dicFiles); + + // Initialise the list view + m_pFileList->setSelectionMode(QListView::Extended); + m_pFileList->addColumn("File Path"); + + // Sort only when asked to by the user + if (Config().getAutoSortFiles()) + m_pFileList->setSortColumn(0); + else + m_pFileList->setSortColumn(m_pFileList->columns() + 1); + + // Add file/directory/tree when the appropriate button is clicked + connect(m_pAddFilesButton, SIGNAL(clicked()), this, + SLOT(slotAddFiles())); + connect(m_pAddDirButton, SIGNAL(clicked()), this, SLOT(slotAddDir())); + connect(m_pAddTreeButton, SIGNAL(clicked()), this, SLOT(slotAddTree())); + + // Remove selected files/directory/tree when the appropriate button is + // clicked + connect(m_pRemSelButton, SIGNAL(clicked()), this, SLOT(slotRemSel())); + connect(m_pRemDirButton, SIGNAL(clicked()), this, SLOT(slotRemDir())); + connect(m_pRemTreeButton, SIGNAL(clicked()), this, SLOT(slotRemTree())); + + // Hide/show files according to filter + connect(m_pFilterButton, SIGNAL(clicked()), this, SLOT(slotFilter())); + connect(m_pShowAllButton, SIGNAL(clicked()), this, SLOT(slotShowAll())); + + // Close the dialog when OK/Cancel are clicked + connect(m_pOKButton, SIGNAL(clicked()), this, SLOT(accept())); + connect(m_pCancelButton, SIGNAL(clicked()), this, SLOT(reject())); + + // Fill the list with the project's files + m_pFileList->setUpdatesEnabled(false); + m_pProj->loadFileList(this); + m_pFileList->setUpdatesEnabled(true); + m_pFileList->triggerUpdate(); +} + +/** + * Class destructor. + */ +ProjectFilesDlg::~ProjectFilesDlg() +{ + delete m_pScanner; +} + +/** + * Adds a single entry to the file list. + * Implements the addItem() virtual method of the FileListTarget base + * class. When a ProjectFilesDlg object is given as a parameter to + * ProjectManager::fillList(), this method is called for each file included + * in the project. A new list item is created, containing the file's path, + * and is added to the list. + * @param sFilePath The full path of a source file + */ +void ProjectFilesDlg::addItem(const QString& sFilePath) +{ + QListViewItem* pItem; + + pItem = new QListViewItem(m_pFileList, m_pLastItem); + pItem->setText(0, sFilePath); + m_pLastItem = pItem; + m_dicFiles.insert(sFilePath, pItem); +} + +/** + * Retrieves the first file path in the list. + * Imlpements the firstItem() virtual method of the FileListSource base + * class. + * @param sFilePath Contains the file path, upon successful return + * @return bool true if successful, false if the list is empty + */ +bool ProjectFilesDlg::firstItem(QString& sFilePath) +{ + m_pItrItem = m_pFileList->firstChild(); + return nextItem(sFilePath); +} + +/** + * Retrieves the next file path in the list. + * Imlpements the nextItem() virtual method of the FileListSource base + * class. The function requires that firstItem() will be called to begin an + * iteration through the file paths. + * @param sFilePath Contains the file path, upon successful return + * @return bool true if successful, false if no more items are + * available + */ +bool ProjectFilesDlg::nextItem(QString& sFilePath) +{ + if (m_pItrItem == NULL) + return false; + + sFilePath = m_pItrItem->text(0); + m_pItrItem = m_pItrItem->nextSibling(); + return true; +} + +/** + * Notifies the user on the progress of a directory scan (when adding a new + * directory), and, if finished, allows the user to add these files to the + * project. + * @param pEvent The event object + */ +void ProjectFilesDlg::customEvent(QCustomEvent* pEvent) +{ + DirScanEvent* pDSE; + QString sMsg; + + // Process only directory scan progress events + if (((uint)pEvent->type()) != DirScanEvent::EventId) + return; + + pDSE = (DirScanEvent*)pEvent; + + // Check if the scan has terminated + if (!pDSE->m_bFinished) { + // Create the scan progress dialog, if required + if (m_pScanDlg == NULL) { + m_pScanDlg = new ScanProgressDlg(this); + connect(m_pScanDlg, SIGNAL(cancelled()), this, + SLOT(slotCancelDirScan())); + } + + // Set progress indication + m_pScanDlg->addFiles(pDSE->m_nFiles); + return; + } + + // Destroy the scan progress dialog + delete m_pScanDlg; + m_pScanDlg = NULL; + + // Verify the thread has terminated + m_pScanner->wait(500); + if (!m_pScanner->finished()) + m_pScanner->terminate(); + + // Do nothing if the operation was cancelled + if (m_pScanner->wasCancelled()) + return; + + // Abort if no files were found + if (pDSE->m_nFiles == 0) { + KMessageBox::sorry(0, "No files were found"); + return; + } + + // Prompt the user for the files to add + sMsg.sprintf(i18n("Would you like to add %d files to your project?"), + pDSE->m_nFiles); + if (KMessageBox::questionYesNo(0, sMsg) == KMessageBox::No) + return; + + // Add the files to the list + const QStringList& slFiles = m_pScanner->getFiles(); + QStringList::const_iterator itr; + + for (itr = slFiles.begin(); itr != slFiles.end(); ++itr) + addItem(*itr); +} + +/** + * Removes a single item from the file list. + */ +void ProjectFilesDlg::removeItem(QListViewItem* pItem) +{ + m_dicFiles.remove(pItem->text(0)); + delete pItem; +} + +/** + * Adds a list of files to the project. + * Prompts the user for source files, and adds the selected files to the + * current project. + */ +void ProjectFilesDlg::slotAddFiles() +{ + QStringList slFiles; + QStringList::const_iterator itr; + + // Prompt the user + slFiles = KFileDialog::getOpenFileNames(m_pProj->getSourceRoot(), + m_pProj->getFileTypes()); + + // Add the selected files, skipping existing entries + for (itr = slFiles.begin(); itr != slFiles.end(); ++itr) { + if (m_dicFiles.find(*itr) == NULL) + addItem(*itr); + } +} + +/** + * Adds all source files in a given directory to the project. + * Prompts the user for a directory, and adds all files matching the + * project's pattern to the current project. + * Note that only source files in the selected directory are added, i.e., the + * search does not descend to sub-directories. + */ +void ProjectFilesDlg::slotAddDir() +{ + QString sDir; + QStringList slFiles; + QStringList::const_iterator itr; + + // Prompt the user for a directory + sDir = KFileDialog::getExistingDirectory(m_pProj->getSourceRoot()); + if (sDir.isEmpty()) + return; + + // Search for source files in this directory + m_pScanner->start(sDir, m_pProj->getFileTypes(), false); +} + +/** + * Adds all source files in a given file system tree to the project. + * Prompts the user for a directory, and adds all files matching the + * project's pattern to the current project. + * Note that source files are searched for in the given directory, as well as + * in any of its sub-directories. + */ +void ProjectFilesDlg::slotAddTree() +{ + QString sDir; + QStringList slFiles; + QStringList::const_iterator itr; + + // Prompt the user for a directory + sDir = KFileDialog::getExistingDirectory(m_pProj->getSourceRoot()); + if (sDir.isEmpty()) + return; + + // Search for source files in this directory + m_pScanner->start(sDir, m_pProj->getFileTypes(), true); +} + +/** + * Removes the selected files from the project. + */ +void ProjectFilesDlg::slotRemSel() +{ + QListViewItem* pItem, * pPrevItem; + + // Prompt the user before removing the files + if (KMessageBox::questionYesNo(0, i18n("Are you sure you want to remove " + "the selected files from the project?")) == KMessageBox::No) { + return; + } + + // Remove the selected files + pItem = m_pFileList->firstChild(); + while (pItem != NULL) { + pPrevItem = pItem; + pItem = pItem->nextSibling(); + + if (pPrevItem->isSelected()) + removeItem(pPrevItem); + } +} + +/** + * Removes all source files in a directory from the project. + */ +void ProjectFilesDlg::slotRemDir() +{ + QString sDir, sFilePath; + QListViewItem* pItem, * pPrevItem; + + // Prompt the user for a directory + sDir = KFileDialog::getExistingDirectory(m_pProj->getSourceRoot()); + if (sDir.isEmpty()) + return; + + // Confirm the directory removal + if (KMessageBox::questionYesNo(0, i18n("Are you sure you want to remove " + "the selected directory from the project?")) == KMessageBox::No) { + return; + } + + // Remove the files under the selected directory + pItem = m_pFileList->firstChild(); + while (pItem != NULL) { + pPrevItem = pItem; + pItem = pItem->nextSibling(); + + // Check if the file is under the selected directory + sFilePath = pPrevItem->text(0); + if (sFilePath.left(sFilePath.findRev('/') + 1) == sDir) + removeItem(pPrevItem); + } +} + +/** + * Removes all source files in a directory or any of its sub-directories from + * the project. + */ +void ProjectFilesDlg::slotRemTree() +{ + QString sDir, sFilePath; + QListViewItem* pItem, * pPrevItem; + + // Prompt the user for a directory + sDir = KFileDialog::getExistingDirectory(m_pProj->getSourceRoot()); + if (sDir.isEmpty()) + return; + + // Confirm the directory removal + if (KMessageBox::questionYesNo(0, i18n("Are you sure you want to remove " + "all files in the selected tree from the project?")) == + KMessageBox::No) { + return; + } + + // Remove the files under the selected directory + pItem = m_pFileList->firstChild(); + while (pItem != NULL) { + pPrevItem = pItem; + pItem = pItem->nextSibling(); + + // Check if the file is under the selected directory + sFilePath = pPrevItem->text(0); + if (sFilePath.startsWith(sDir)) + removeItem(pPrevItem); + } +} + +/** + * Filter files according to a pattern. + * Hides all entries in the file list, except for those that match a given + * pattern. + */ +void ProjectFilesDlg::slotFilter() +{ + QString sFilter; + QListViewItem* pItem; + + // Get the user's filter string + sFilter = m_pFilterEdit->text().stripWhiteSpace(); + if (sFilter.isEmpty()) + return; + + // Create the regular expression + QRegExp reFilter(sFilter); + reFilter.setWildcard(true); + + // Iterate over the list entries, and hide all items not matching the + // filter string + pItem = m_pFileList->firstChild(); + while (pItem != NULL) { + if (reFilter.search(pItem->text(0)) == -1) { + pItem->setVisible(false); + pItem->setSelectable(false); + } + + pItem = pItem->nextSibling(); + } +} + +/** + * Shows all entries in the file list, after a filter has been applied. + */ +void ProjectFilesDlg::slotShowAll() +{ + QListViewItem* pItem; + + // Iterate over the list entries, and make all items visible + pItem = m_pFileList->firstChild(); + while (pItem != NULL) { + pItem->setVisible(true); + pItem->setSelectable(true); + pItem = pItem->nextSibling(); + } +} + +/** + * Stops a directory scan process. + * This slot is called when the user clicks on the "Cancel" button in the + * scan progress dialog. + */ +void ProjectFilesDlg::slotCancelDirScan() +{ + m_pScanner->cancel(); +} + +#include "projectfilesdlg.moc" diff --git a/src/projectfilesdlg.h b/src/projectfilesdlg.h new file mode 100644 index 0000000..9c6d791 --- /dev/null +++ b/src/projectfilesdlg.h @@ -0,0 +1,104 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef PROJECTFILESDLG_H +#define PROJECTFILESDLG_H + +#include <qwidget.h> +#include <projectfileslayout.h> +#include <qdict.h> +#include "project.h" + +class DirScanner; +class ScanProgressDlg; + +/** + * A dialog to manipulate the project's files. + * The dialog allows the user to add source files to the current project, or + * remove files from it. The main widget of the dialog is a list view, that + * displays all files currently in the project. When files are added or + * removed, this list view is updated. The project, however, is only modified + * if the user closes the dialog using the "OK" button. + * Since searches through a list view are very slow, the class also maintains + * a QDict object, that connects file names with their respective list items. + * This dictionary is used to ensure duplicated items are not added to the + * list. + * @author Elad Lahav + */ + +class ProjectFilesDlg : public ProjectFilesLayout, public FileListTarget, + public FileListSource +{ + Q_OBJECT + +public: + ProjectFilesDlg(Project*, QWidget* pParent = 0, const char* szName = 0); + ~ProjectFilesDlg(); + + virtual void addItem(const QString&); + virtual bool firstItem(QString&); + virtual bool nextItem(QString&); + +protected: + virtual void customEvent(QCustomEvent*); + +private: + /** The project to manipulate. */ + Project* m_pProj; + + /** Holds all file paths in a quickly searchable format (for duplicate + entries lookup). */ + QDict<QListViewItem> m_dicFiles; + + /** A thread object to a-synchronously scan directories for source files + to add to the project. */ + DirScanner* m_pScanner; + + /** Displays the progress of a directory scan operation. */ + ScanProgressDlg* m_pScanDlg; + + /** A file list item that serves as an iterator. */ + QListViewItem* m_pItrItem; + + /** The last item added. */ + QListViewItem* m_pLastItem; + + void removeItem(QListViewItem*); + +private slots: + void slotAddFiles(); + void slotAddDir(); + void slotAddTree(); + void slotRemSel(); + void slotRemDir(); + void slotRemTree(); + void slotFilter(); + void slotShowAll(); + void slotCancelDirScan(); +}; + +#endif diff --git a/src/projectfileslayout.ui b/src/projectfileslayout.ui new file mode 100644 index 0000000..a78af7e --- /dev/null +++ b/src/projectfileslayout.ui @@ -0,0 +1,201 @@ +<!DOCTYPE UI><UI version="3.1" stdsetdef="1"> +<class>ProjectFilesLayout</class> +<widget class="QDialog"> + <property name="name"> + <cstring>ProjectFilesLayout</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>585</width> + <height>480</height> + </rect> + </property> + <property name="caption"> + <string>Project Files</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout5</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout4</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLineEdit"> + <property name="name"> + <cstring>m_pFilterEdit</cstring> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pFilterButton</cstring> + </property> + <property name="text"> + <string>Filter</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pShowAllButton</cstring> + </property> + <property name="text"> + <string>Show All</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QListView"> + <property name="name"> + <cstring>m_pFileList</cstring> + </property> + </widget> + </vbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout5</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox1</cstring> + </property> + <property name="title"> + <string>Add</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pAddFilesButton</cstring> + </property> + <property name="text"> + <string>Files...</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pAddDirButton</cstring> + </property> + <property name="text"> + <string>Directory...</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pAddTreeButton</cstring> + </property> + <property name="text"> + <string>Tree...</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox2</cstring> + </property> + <property name="title"> + <string>Remove</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pRemSelButton</cstring> + </property> + <property name="text"> + <string>Selected</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pRemDirButton</cstring> + </property> + <property name="text"> + <string>Directory...</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pRemTreeButton</cstring> + </property> + <property name="text"> + <string>Tree...</string> + </property> + </widget> + </vbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>21</width> + <height>118</height> + </size> + </property> + </spacer> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox3</cstring> + </property> + <property name="title"> + <string></string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pOKButton</cstring> + </property> + <property name="text"> + <string>OK</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pCancelButton</cstring> + </property> + <property name="text"> + <string>Cancel</string> + </property> + </widget> + </vbox> + </widget> + </vbox> + </widget> + </hbox> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/src/projectmanager.cpp b/src/projectmanager.cpp new file mode 100644 index 0000000..998b4a5 --- /dev/null +++ b/src/projectmanager.cpp @@ -0,0 +1,180 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <kmessagebox.h> +#include <klocale.h> +#include "projectmanager.h" +#include "project.h" +#include "kscopeconfig.h" + +/** + * Class constructor. + */ +ProjectManager::ProjectManager() : m_pCurProj(NULL) +{ +} + +/** + * Class destructor. + */ +ProjectManager::~ProjectManager() +{ + close(); +} + +/** + * Creates a project's directory, and associates this directory with the + * current object. This directory is created under the given path, and using + * the project's name (which, thus, has to be a legal file name). + * Note: this function attempts to create a new directory, so the given path + * and name must not lead to an existing one. + * @param sName The project's name + * @param sPath The parent directory under which to create the + * project's directory + * @param opt A structure containing project options + * @return true if successful, false otherwise + */ +bool ProjectManager::create(const QString& sName, const QString& sPath, + const ProjectBase::Options& opt, QString& sProjDir) +{ + QDir dir(sPath); + QString sParentPath; + QString sDirName = sName; + QString sMsg; + + // Handle requests for a hidden .cscope directory + if (dir.dirName() == ".cscope") { + sParentPath = QDir::cleanDirPath(dir.absPath()); + sParentPath = sParentPath.section('/', 0, -2); + dir.cd(sParentPath); + sDirName = ".cscope"; + } + + // The parent directory must exist + if (!dir.exists()) { + sMsg = i18n("The requested parent directory (%1) does not exist"). + arg(sParentPath); + KMessageBox::error(0, sMsg); + return false; + } + + // Make sure the directory doesn't exist + if (dir.exists(sDirName)) { + sMsg = i18n("Cannot create a project inside an existing directory " + "(%1/%2)").arg(dir.canonicalPath()).arg(sDirName); + KMessageBox::error(0, sMsg); + return false; + } + + // Try to create the projcet's directory + if (!dir.mkdir(sDirName, false) || !dir.cd(sDirName, false)) { + sMsg = i18n("Failed to create the project directory (%1/%2)"). + arg(dir.canonicalPath()).arg(sDirName); + KMessageBox::error(0, sMsg); + return false; + } + + if (!Project::create(sName, dir.absPath(), opt)) + return false; + + sProjDir = dir.path(); + return true; +} + +/** + * Opens a project and makes it the current one. + * @param sPath The directory containing the project's files + * @return true if successful, false otherwise + */ +bool ProjectManager::open(const QString& sPath) +{ + Project* pProj; + + // Close the current project + close(); + + // Try to open the new project + pProj = new Project(); + if (!pProj->open(sPath)) { + delete pProj; + return false; + } + + // Add to the list of recently opened projects + Config().addRecentProject(sPath); + + // Project opened successfully + m_pCurProj = pProj; + return true; +} + +/** + * Opens a Cscope.out file as a temporary project. + * @param sFilePath The full path of the Cscope.out file + * @return true if successful, false otherwise + */ +bool ProjectManager::openCscopeOut(const QString& sFilePath) +{ + ProjectBase* pProj; + + // Close the current project + close(); + + // Try to open the new project + pProj = new ProjectBase(); + if (!pProj->open(sFilePath)) { + delete pProj; + return false; + } + + // Add to the list of recently opened projects + Config().addRecentProject(sFilePath); + + // Project opened successfully + m_pCurProj = pProj; + return true; +} + +/** + * Performs clean-up on the project's variables, and detaches the associated + * directory. + */ +void ProjectManager::close() +{ + if (m_pCurProj) { + delete m_pCurProj; + m_pCurProj = NULL; + } +} + +QString ProjectManager::getProjName() const +{ + if (!m_pCurProj) + return i18n("No Project"); + + return m_pCurProj->getName(); +} diff --git a/src/projectmanager.h b/src/projectmanager.h new file mode 100644 index 0000000..c78a2c9 --- /dev/null +++ b/src/projectmanager.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef PROJECTMANAGER_H +#define PROJECTMANAGER_H + +#include "projectbase.h" + +/** + * @author Elad Lahav + */ +class ProjectManager : public QObject +{ +public: + ProjectManager(); + virtual ~ProjectManager(); + + bool create(const QString&, const QString&, const ProjectBase::Options&, + QString&); + bool open(const QString&); + bool openCscopeOut(const QString&); + void close(); + QString getProjName() const; + + ProjectBase* curProject() const { return m_pCurProj; } + +private: + /** The current project (NULL if no project is open). */ + ProjectBase* m_pCurProj; +}; + +#endif diff --git a/src/query_locked.png b/src/query_locked.png Binary files differnew file mode 100644 index 0000000..25db6b2 --- /dev/null +++ b/src/query_locked.png diff --git a/src/query_unlocked.png b/src/query_unlocked.png Binary files differnew file mode 100644 index 0000000..3a1a0d3 --- /dev/null +++ b/src/query_unlocked.png diff --git a/src/querypage.cpp b/src/querypage.cpp new file mode 100644 index 0000000..502747b --- /dev/null +++ b/src/querypage.cpp @@ -0,0 +1,211 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qfile.h> +#include <klocale.h> +#include "querypage.h" +#include "queryview.h" +#include "queryviewdriver.h" + +const char* QUERY_TYPES[][2] = { + { "References to ", "REF " }, + { "Definition of ", "DEF " }, + { "Functions called by ", "<-- " }, + { "Functions calling ", "-->" }, + { "Search for ", "TXT " }, + { "", "" }, + { "EGrep Search for ", "GRP " }, + { "Files named ", "FIL " }, + { "Files #including ", "INC " }, + { "Query", "Query" } +}; + +/** + * Class constructor. + * @param pParent The parent widget + * @param szName The widget's name + */ +QueryPage::QueryPage(QWidget* pParent, const char * szName) : + QueryPageBase(pParent, szName), + m_nType(CscopeFrontend::None) +{ + m_pView = new QueryView(this); + m_pDriver = new QueryViewDriver(m_pView, this); + + connect(m_pView, SIGNAL(lineRequested(const QString&, uint)), this, + SIGNAL(lineRequested(const QString&, uint))); + + // Set colours and font + applyPrefs(); +} + +/** + * Class destructor. + */ +QueryPage::~QueryPage() +{ +} + +/** + * Runs a query, using the current page to display the results. + * @param nType The type of the query + * @param sText The text of the query + * @param bCase true for case-sensitive queries, false otherwise + */ +void QueryPage::query(uint nType, const QString& sText, bool bCase) +{ + m_nType = nType; + m_sText = sText; + m_bCase = bCase; + m_sName = getCaption(); + + m_pDriver->query(nType, sText, bCase); +} + +/** + * Re-runs the last query. + */ +void QueryPage::refresh() +{ + m_pView->clear(); + if (!m_sText.isEmpty()) + m_pDriver->query(m_nType, m_sText, m_bCase); +} + +/** + * Resets the query page by deleting all records. + */ +void QueryPage::clear() +{ + m_pView->clear(); + m_nType = CscopeFrontend::None; + m_sText = QString(); + m_sName = QString(); +} + +/** + * @return true if a query is currently running in this page, false otherwise + */ +bool QueryPage::isRunning() +{ + return m_pDriver->isRunning(); +} + +/** + * Constructs a caption for this page, based on the query's type and text. + * @param bBrief true to use a shortened version of the caption, false + * (default) for the full version + * @return The caption for this page + */ +QString QueryPage::getCaption(bool bBrief) const +{ + return QString(QUERY_TYPES[m_nType][bBrief ? 1 : 0] + m_sText); +} + +/** + * Creates a new query result item. + * @param sFile The file name + * @param sFunc The function defining the scope of the result + * @param sLine The line number + * @param sText The contents of the line + */ +void QueryPage::addRecord(const QString& sFile, const QString& sFunc, + const QString& sLine, const QString& sText) +{ + new QListViewItem(m_pView, sFile, sFunc, sLine, sText); +} + +/** + * Creates a unique file name for saving the contents of the query page. + * @param sProjPath The full path of the project directory + * @return The unique file name to use + */ +QString QueryPage::getFileName(const QString& sProjPath) const +{ + QString sFileName, sFileNameBase; + int i = 0; + + // Do nothing if not initialised + if (m_sName.isEmpty()) + return ""; + + // Create a unique file name + sFileNameBase = m_sName; + sFileNameBase.replace(' ', '_'); + do { + sFileName = sFileNameBase + QString::number(++i); + } while (QFile(sProjPath + "/" + sFileName).exists()); + + return sFileName; +} + +/** + * Reads query parameters from a file. + * This mehtod is used as part of the loading process. + * @param str A text stream set to the correct place in the file + * @return true if successful, false otherwise + */ +bool QueryPage::readHeader(QTextStream& str) +{ + QString sTemp; + + // Read the query name + m_sName = str.readLine(); + if (m_sName == QString::null || m_sName.isEmpty()) + return false; + + // Read the query's type + sTemp = str.readLine(); + if (sTemp == QString::null || sTemp.isEmpty()) + return false; + + // Convert the type string to an integer + m_nType = sTemp.toUInt(); + if (m_nType >= CscopeFrontend::None) { + m_nType = CscopeFrontend::None; + return false; + } + + // Read the query's text + m_sText = str.readLine(); + if (m_sText == QString::null || m_sText.isEmpty()) + return false; + + return true; +} + +/** + * Writes query parameters to a file. + * This mehtod is used as part of the storing process. + * @param str A text stream set to the correct place in the file + */ +void QueryPage::writeHeader(QTextStream& str) +{ + str << m_sName << "\n" << m_nType << "\n" << m_sText << "\n"; +} + +#include "querypage.moc" diff --git a/src/querypage.h b/src/querypage.h new file mode 100644 index 0000000..59d6ea2 --- /dev/null +++ b/src/querypage.h @@ -0,0 +1,86 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef QUERYPAGE_H +#define QUERYPAGE_H + +#include <qwidget.h> +#include <qlistview.h> +#include <qregexp.h> +#include "querypagebase.h" +#include "cscopefrontend.h" + +class QueryViewDriver; + +/** + * A QueryWidget page that runs and displays Cscope queries. + * The page uses a QueryViewDriver object to run queries, and an embedded + * QueryView widget for displaying query results. + * @author Elad Lahav + */ +class QueryPage : public QueryPageBase +{ + Q_OBJECT + +public: + QueryPage(QWidget* pParent = 0, const char* szName = 0); + ~QueryPage(); + + void query(uint, const QString&, bool); + void refresh(); + void clear(); + bool isRunning(); + + virtual QString getCaption(bool bBrief = false) const; + +protected: + virtual void addRecord(const QString&, const QString&, const QString&, + const QString&); + virtual QString getFileName(const QString&) const; + virtual bool readHeader(QTextStream&); + virtual void writeHeader(QTextStream&); + +private: + /** The type of query whose results are listed on this page. */ + uint m_nType; + + /** The text given as a parameter to the query. */ + QString m_sText; + + /** Whether the query is case-sensitive. */ + bool m_bCase; + + /** A formatted caption for this query, including the type of query and + its text. */ + QString m_sName; + +private: + /** Runs Cscope queries whose results are displayed in this page. */ + QueryViewDriver* m_pDriver; +}; + +#endif diff --git a/src/querypagebase.cpp b/src/querypagebase.cpp new file mode 100644 index 0000000..08cbe6d --- /dev/null +++ b/src/querypagebase.cpp @@ -0,0 +1,194 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qfile.h> +#include "querypagebase.h" +#include "queryview.h" +#include "kscopeconfig.h" + +#define FILE_VERSION "VERSION=2" + +/** + * Class constructor. + * @param pParent The parent widget + * @param szName The widget's name + */ +QueryPageBase::QueryPageBase(QWidget* pParent, const char* szName) : + QHBox(pParent, szName), + m_bLocked(false) +{ +} + +/** + * Class destructor. + */ +QueryPageBase::~QueryPageBase() +{ +} + +/** + * Sets the list's colours and font, according the user's preferences. + */ +void QueryPageBase::applyPrefs() +{ + // Apply colour settings + m_pView->setPaletteBackgroundColor(Config().getColor( + KScopeConfig::QueryWindowBack)); + m_pView->setPaletteForegroundColor(Config().getColor( + KScopeConfig::QueryWindowFore)); + m_pView->setFont(Config().getFont(KScopeConfig::QueryWindow)); +} + +/** + * Restores a locked query from the given query file. + * NOTE: The query file is deleted when loading is complete. + * @param sProjPath The full path of the project directory + * @param sFileName The name of the query file to load + * @return true if successful, false otherwise + */ +bool QueryPageBase::load(const QString& sProjPath, const QString& sFileName) +{ + QString sTemp, sFile, sFunc, sLine, sText; + int nState; + + // Try to open the query file for reading + QFile file(sProjPath + "/" + sFileName); + if (!file.open(IO_ReadOnly)) + return false; + + { + // Use a new scope for the QTextStream object, to ensure its + // destruction before the file is deleted + QTextStream str(&file); + + // Make sure the file's version is correct + sTemp = str.readLine(); + if (sTemp != FILE_VERSION) { + file.remove(); + return false; + } + + // Try to read the file header + if (!readHeader(str)) + return false; + + // Read query records + sTemp = str.readLine(); + nState = 0; + while (sTemp != QString::null) { + switch (nState) { + // File path + case 0: + sFile = sTemp; + break; + + // Function name + case 1: + sFunc = sTemp; + break; + + // Line number + case 2: + sLine = sTemp; + break; + + // Text string + case 3: + sText = sTemp; + addRecord(sFile, sFunc, sLine, sText); + break; + } + + nState = (nState + 1) % 4; + sTemp = str.readLine(); + } + } + + // Delete the query file + file.remove(); + + return true; +} + +/** + * Writes the contents of the page to a file. + * This method is called for pages that shoukld be stored before the owner + * project is closed (@see shouldSave()). + * @param sProjPath The full path of the project directory + * @param sFileName Holds the file name to which the page was saved, upon + * return + * @return true if successful, false otherwise + */ +bool QueryPageBase::save(const QString& sProjPath, QString& sFileName) +{ + QListViewItemIterator itr(m_pView); + + // Get the file name to use + sFileName = getFileName(sProjPath); + if (sFileName.isEmpty()) + return false; + + // Open the query file for writing + QFile file(sProjPath + "/" + sFileName); + if (!file.open(IO_WriteOnly)) + return false; + + QTextStream str(&file); + + // Write the version string + str << FILE_VERSION << "\n"; + + writeHeader(str); + + // Write all records + for(; itr.current(); ++itr) { + str << itr.current()->text(0) << "\n" + << itr.current()->text(1) << "\n" + << itr.current()->text(2) << "\n" + << itr.current()->text(3) << "\n"; + } + + return true; +} + +/** + * Selects the next record in the view. + */ +void QueryPageBase::selectNext() +{ + m_pView->selectNext(); +} + +/** + * Selects the previous record in the view. + */ +void QueryPageBase::selectPrev() +{ + m_pView->selectPrev(); +} + +#include "querypagebase.moc" diff --git a/src/querypagebase.h b/src/querypagebase.h new file mode 100644 index 0000000..8603874 --- /dev/null +++ b/src/querypagebase.h @@ -0,0 +1,148 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef QUERYPAGEBASE_H +#define QUERYPAGEBASE_H + +#include <qhbox.h> + +class QueryView; + +/** + * Defines a page in a QueryWidget's tab widget. + * This is a abstract base class for QueryPage and HistoryPage. It defines + * the common behaviour for all pages, which includes appearance, display + * of tab text, page locking, storage and retrieval of information to + * and from files and basic navigation. + * Each page embeds a list widget derived from QueryView. The actual type + * of widget is defined by the different page classes. + * @author Elad Lahav + */ +class QueryPageBase : public QHBox +{ +Q_OBJECT +public: + QueryPageBase(QWidget* pParent = 0, const char* szName = 0); + ~QueryPageBase(); + + void applyPrefs(); + bool load(const QString&, const QString&); + bool save(const QString&, QString&); + void selectNext(); + void selectPrev(); + + + /** + * Determines whether this page can be locked. + * Can be used by inheriting classes to define non-lockable pages. + * @return Always true + */ + virtual bool canLock() { return true; } + + /** + * Locks or unlocks this page. + * @param bLocked true to lock the page, false to unlock it. + */ + void setLocked(bool bLocked) { m_bLocked = bLocked; } + + /** + * Determines whether this page is locked. + * @return true if the page is locked, false otherwise + */ + bool isLocked() { return m_bLocked; } + + /** + * Determines whether this page should be saved when the project is closed. + * By default, pages are saved if and only if they are locked. + * @return true to save the page, false otherwise + */ + virtual bool shouldSave() const { return m_bLocked; }; + + /** + * Constructs a caption for this page. + * The caption appears in the page's tab button and as the page's + * tooltip. + * @param bBrief true to generate a brief caption, false otherwise + * @return The page's title + */ + virtual QString getCaption(bool bBrief = false) const = 0; + +signals: + /** + * Emitted when a record is selected in the view widget. + * @param sFile The "File" field of the selected record + * @param nLine The "Line" field of the selected record + */ + void lineRequested(const QString& sFile, uint nLine); + +protected: + /** The embedded list. */ + QueryView* m_pView; + + /** Indicates whether this page is locked. A locked page is never + overriden by new data, and is also saved to a disc file when the + session is closed. */ + bool m_bLocked; + + /** + * Creates a new list item and adds it to the embedded view. + * This method is used to add records read from a stored file. + * @param sFile The "File" field of the record + * @param sFunc The "Function" field of the record + * @param sLine The "Line" field of the record + * @param sText The "Text" field of the record + */ + virtual void addRecord(const QString& sFile, const QString& sFunc, + const QString& sLine, const QString& sText) = 0; + + /** + * Creates a file path to store this page. + * The path is composed of the project's path and a unique file name + * in that directory. + * @param sProjPath The project's directory + * @return The page's file path + */ + virtual QString getFileName(const QString& sProjPath) const = 0; + + /** + * Tries to read the file header of a stored page. + * The contents of the header differ among inheriting classes. + * @param str A text stream initialised to the open page file + * @return true if the header was read successfully and contains the + * expected information, false otherwise + */ + virtual bool readHeader(QTextStream& str) = 0; + + /** + * Writes a header to a page's file. + * The contents of the header differ among inheriting classes. + * @param str A text stream initialised to the open page file + */ + virtual void writeHeader(QTextStream& str) = 0; +}; + +#endif diff --git a/src/queryresultsmenu.cpp b/src/queryresultsmenu.cpp new file mode 100644 index 0000000..74bcdb4 --- /dev/null +++ b/src/queryresultsmenu.cpp @@ -0,0 +1,170 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <klocale.h> +#include "queryresultsmenu.h" + +/** + * Class constructor. + * @param pParent Parent widget + * @param szName Optional object name + */ +QueryResultsMenu::QueryResultsMenu(QWidget* pParent, const char* szName) : + QPopupMenu(pParent, szName), + m_pItem(NULL) +{ + // Create the menu + insertItem(i18n("&View Source"), this, SLOT(slotViewSource()), 0, + ViewSource); + insertItem(i18n("Find &Definition"), this, SLOT(slotFindDef()), 0, + FindDef); + insertSeparator(); + insertItem(i18n("&Copy"), this, SLOT(slotCopy()), 0, Copy); + insertSeparator(); + insertItem(i18n("&Filter..."), this, SLOT(slotFilter()), 0, Filter); + insertItem(i18n("&Show All"), this, SIGNAL(showAll()), 0, ShowAll); + insertSeparator(); + insertItem(i18n("&Remove Item"), this, SLOT(slotRemove()), 0, Remove); +} + +/** + * Class destructor. + */ +QueryResultsMenu::~QueryResultsMenu() +{ +} + +/** + * Displays the popup-menu at the requested coordinates. + * @param pItem The item on which the menu was requested + * @param ptPos The requested position for the menu + * @param nCol The column over which the menu was requested, -1 if no + * column is associated with the request + */ +void QueryResultsMenu::slotShow(QListViewItem* pItem, const QPoint& ptPos, + int nCol) +{ + // Save the requested item and column number to use in signals + m_pItem = pItem; + m_nCol = nCol; + + if (m_pItem == NULL) { + // No item selected, disable everything but the "Filter" and "Show All" + // items + setItemEnabled(ViewSource, false); + setItemEnabled(FindDef, false); + setItemEnabled(Copy, false); + setItemEnabled(Remove, false); + } + else { + // Item selected, enable record-specific actions + setItemEnabled(ViewSource, true); + setItemEnabled(Copy, true); + setItemEnabled(Remove, true); + + // The "Find Definition" item should only be enabled if the mouse + // was clicked over a valid function name + setItemEnabled(FindDef, (m_nCol == 0) && + (m_pItem->text(0) != "<global>")); + + // Set menu contents according to the column number + switch (m_nCol) { + case 0: + changeItem(Copy, "&Copy Function"); + break; + + case 1: + changeItem(Copy, "&Copy File"); + break; + + case 2: + changeItem(Copy, "&Copy Line Number"); + break; + + case 3: + changeItem(Copy, "&Copy Text"); + break; + + default: + m_nCol = 0; + } + } + + // Show the menu + popup(ptPos); +} + +/** + * Emits the viewSource() signal. + * This slot is activated when the "View Source" item is selected. + */ +void QueryResultsMenu::slotViewSource() +{ + if (m_pItem != NULL) + emit viewSource(m_pItem); +} + +/** + * Emits the findDef() signal. + * This slot is activated when the "Find Definition" item is selected. + */ +void QueryResultsMenu::slotFindDef() +{ + if (m_pItem != NULL) + emit findDef(m_pItem->text(0)); +} + +/** + * Emits the copy() signal. + * This slot is activated when the "Copy [Column]" item is selected. + */ +void QueryResultsMenu::slotCopy() +{ + if (m_pItem != NULL) + emit copy(m_pItem, m_nCol); +} + +/** + * Emits the filter() signal. + * This slot is activated when the "Filter..." item is selected. + */ +void QueryResultsMenu::slotFilter() +{ + emit filter(m_nCol); +} + +/** + * Emits the remove() signal. + * This slot is activated when the "Remove" item is selected. + */ +void QueryResultsMenu::slotRemove() +{ + if (m_pItem != NULL) + emit remove(m_pItem); +} + +#include "queryresultsmenu.moc" diff --git a/src/queryresultsmenu.h b/src/queryresultsmenu.h new file mode 100644 index 0000000..099dd2e --- /dev/null +++ b/src/queryresultsmenu.h @@ -0,0 +1,110 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef QUERYRESULTSMENU_H +#define QUERYRESULTSMENU_H + +#include <qpopupmenu.h> +#include <qlistview.h> +#include <qregexp.h> + +/** + * Provides a popup-menu for list views containing query results. + * The popup menu contains commands for copying field text out of items and + * for removing items. + * This class assumes a certain ordering of the list columns. If an owner + * object uses a different configuration, it needs to call setColumns() after + * constructing the object. + * @author Elad Lahav + */ +class QueryResultsMenu : public QPopupMenu +{ + Q_OBJECT + +public: + QueryResultsMenu(QWidget* pParent = 0, const char* szName = 0); + ~QueryResultsMenu(); + +public slots: + void slotShow(QListViewItem*, const QPoint&, int nCol); + +signals: + /** + * Indicates that the "View Source" menu item was selected. + * @param pItem The item for which the menu was displayed + */ + void viewSource(QListViewItem* pItem); + + /** + * Indicates that the "Find Definition" menu item was selected. + * @param sFunc The function to look for + */ + void findDef(const QString& sFunc); + + /** + * Indicates that the "Copy [Column]" menu item was selected. + * @param pItem The item for which the menu was displayed + * @param nCol The requested column + */ + void copy(QListViewItem* pItem, int nCol); + + /** + * Indicates that the "Filter..." menu item was selected. + * @param nCol The column in which to search + */ + void filter(int nCol); + + /** + * Indicates that the "Show All" menu item was selected. + */ + void showAll(); + + /** + * Indicates that the "Remove Item" menu item was selected. + * @param pItem The item for which the menu was displayed + */ + void remove(QListViewItem* pItem); + +private: + /** Menu item IDs. */ + enum { ViewSource, FindDef, Copy, Filter, ShowAll, Remove }; + + /** The item for which the popup menu is provided (cannot be NULL). */ + QListViewItem* m_pItem; + + /** The list column for which the query was invoked. */ + int m_nCol; + +private slots: + void slotViewSource(); + void slotFindDef(); + void slotCopy(); + void slotFilter(); + void slotRemove(); +}; + +#endif diff --git a/src/queryview.cpp b/src/queryview.cpp new file mode 100644 index 0000000..c56a2b0 --- /dev/null +++ b/src/queryview.cpp @@ -0,0 +1,444 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qapplication.h> +#include <qclipboard.h> +#include <klocale.h> +#include "queryview.h" +#include "queryresultsmenu.h" +#include "queryviewdlg.h" +#include "cscopefrontend.h" +#include "searchresultsdlg.h" + +/** + * Class constructor. + * @param pParent The parent widget + * @param szName The name of the widget + */ +QueryView::QueryView(QWidget* pParent, const char* szName) : + QListView(pParent, szName), + m_pLastItem(NULL) +{ + // Create the popup-menu + m_pQueryMenu = new QueryResultsMenu(this); + + // Initialise the list's columns + setAllColumnsShowFocus(true); + addColumn(i18n("Function")); + addColumn(i18n("File")); + addColumn(i18n("Line")); + addColumn(i18n("Text")); + setColumnAlignment(2, Qt::AlignRight); + + setShowSortIndicator(true); + + // A record is selected if it is either double-clicked, or the ENTER + // key is pressed while the record is highlighted + connect(this, SIGNAL(doubleClicked(QListViewItem*)), this, + SLOT(slotRecordSelected(QListViewItem*))); + connect(this, SIGNAL(returnPressed(QListViewItem*)), this, + SLOT(slotRecordSelected(QListViewItem*))); + + // Show the popup-menu when requested + connect(this, + SIGNAL(contextMenuRequested(QListViewItem*, const QPoint&, int)), + m_pQueryMenu, SLOT(slotShow(QListViewItem*, const QPoint&, int))); + + // Handle popup-menu commands + connect(m_pQueryMenu, SIGNAL(viewSource(QListViewItem*)), this, + SLOT(slotRecordSelected(QListViewItem*))); + connect(m_pQueryMenu, SIGNAL(findDef(const QString&)), this, + SLOT(slotFindDef(const QString&))); + connect(m_pQueryMenu, SIGNAL(copy(QListViewItem*, int)), this, + SLOT(slotCopy(QListViewItem*, int))); + connect(m_pQueryMenu, SIGNAL(filter(int)), this, SLOT(slotFilter(int))); + connect(m_pQueryMenu, SIGNAL(showAll()), this, + SLOT(slotShowAll())); + connect(m_pQueryMenu, SIGNAL(remove(QListViewItem*)), this, + SLOT(slotRemoveItem(QListViewItem*))); +} + +/** + * Class destructor. + */ +QueryView::~QueryView() +{ +} + +/** + * Creates a new list item showing a query result record. + * @param sFunc The name of the function + * @param sFile The file path + * @param sLine The line number in the above file + * @param sText The line's text + * @param pParent The parent item (ignored) + */ +void QueryView::addRecord(const QString& sFunc, const QString& sFile, + const QString& sLine, const QString& sText, QListViewItem* /* pParent */) +{ + QListViewItem* pItem; + + pItem = new QueryViewItem(this, m_pLastItem, 2); + pItem->setText(0, sFunc); + pItem->setText(1, sFile); + pItem->setText(2, sLine); + pItem->setText(3, sText); + + m_pLastItem = pItem; +} + +/** + * Selects an item. + * When an item is selected, it is highlighted and made visible. By + * definition, the lineRequested() signal is also emitted. + * This method is used for selecting records programmatically (@see + * selectNext() for example). It has nothing to do with user selection. + * @param pItem The list item to select + */ +void QueryView::select(QListViewItem* pItem) +{ + ensureItemVisible(pItem); + setSelected(pItem, true); + slotRecordSelected(pItem); +} + +/** + * Selects the next record in the list (if one exists). + * The function selects the next item as follows: + * - The first item in the list, if there is no current item + * - The current item, if it is not selected + * - The item immediately below the current item, otherwise + */ +void QueryView::selectNext() +{ + QListViewItem* pItem; + + // Do nothing if the list is empty + if (firstChild() == NULL) + return; + + // Find the next record + pItem = currentItem(); + if (pItem == NULL) { + pItem = firstChild(); + } + else if (pItem->isSelected()) { + pItem = pItem->itemBelow(); + if (pItem == NULL) + return; + } + + // Select the new item + select(pItem); +} + +/** + * Selects the previous record in the list (if one exists). + * The function selects the previous item as follows: + * - The first item in the list, if there is no current item + * - The current item, if it is not selected + * - The item immediately above the current item, otherwise + */ +void QueryView::selectPrev() +{ + QListViewItem* pItem; + + // Do nothing if the list is empty + if (firstChild() == NULL) + return; + + // Find the item immediately above this one + pItem = currentItem(); + if (pItem == NULL) { + pItem = firstChild(); + } + else if (pItem->isSelected()) { + pItem = pItem->itemAbove(); + if (pItem == NULL) + return; + } + + // Select the new item + select(pItem); +} + +/** + * Informs the view that query progress information has been received. + * The view emits the needToShow() signal telling its parent that the widget + * should become visible (if not already so). + */ +void QueryView::queryProgress() +{ + if (!isVisible()) + emit needToShow(); +} + +/** + * Called when a query using this view terminates. + * @param nRecords Number of records generated by the query + */ +void QueryView::queryFinished(uint nRecords, QListViewItem*) +{ + // Auto-select a single record (no need to emit the show() signal in + // that case) + if (nRecords == 1) { + emit select(firstChild()); + return; + } + + // Report a query that has returned an empty record set + if (nRecords == 0) + addRecord(i18n("No results"), "", "", "", NULL); + + // Data is available, instruct the owner object to show the view + emit needToShow(); +} + +/** + * Creates an iterator and initialises it to point to the first _visible_ + * item in the list. + * @return A new iterator initialised to the beginning of the list + */ +QueryView::Iterator QueryView::getIterator() +{ + QListViewItem* pItem; + + for (pItem = firstChild(); pItem != NULL && !pItem->isVisible(); + pItem = pItem->nextSibling()); + + return Iterator(pItem); +} + +/** + * Handles double-click events over the view. + * NOTE: We override this method since the QListView implementation opens + * expandable items. This is undesired for tree-like query views (such as the + * call tree. + * @param pEvent Event description object + */ +void QueryView::contentsMouseDoubleClickEvent(QMouseEvent* pEvent) +{ + QListViewItem* pItem; + + // Handle only left button double-clicks + if (pEvent == NULL || pEvent->button() != LeftButton) + return; + + // Find the clicked item + pItem = itemAt(contentsToViewport(pEvent->pos())); + if (pItem == NULL) + return; + + // Emit the doubleClicked() signal + emit doubleClicked(pItem); +} + +/** + * Emits the lineRequested() signal when a list item is selected. + * This slot is connected to the doubleClicked() and returnPressed() + * signals of the list view. + * @param pItem The selected item + */ +void QueryView::slotRecordSelected(QListViewItem* pItem) +{ + QString sFileName, sLine; + + // Get the file name and line number + sFileName = pItem->text(1); + sLine = pItem->text(2); + + // Do not process the "No results" item + if (!sLine.isEmpty()) + emit lineRequested(sFileName, sLine.toUInt()); +} + +/** + * Looks up the definition of a given function. + * Results are displayed in a popup window. + * This slot is connected to the findDef() signal emitted by the results menu. + * @param sFunc The function to look for + */ +void QueryView::slotFindDef(const QString& sFunc) +{ + QueryViewDlg* pDlg; + + // Create a query view dialogue + pDlg = new QueryViewDlg(QueryViewDlg::DestroyOnSelect, this); + + // Display a line when it is selected in the dialogue + connect(pDlg, SIGNAL(lineRequested(const QString&, uint)), this, + SIGNAL(lineRequested(const QString&, uint))); + + // Start the query + pDlg->query(CscopeFrontend::Definition, sFunc); +} + +/** + * Copies the text of the requested field (item+column) to the clipboard. + * This slot is connected to the copy() signal of the QueryResultsMenu object. + * @param pItem The item from which to copy + * @param nCol The index of the item field to copy + */ +void QueryView::slotCopy(QListViewItem* pItem, int nCol) +{ + QApplication::clipboard()->setText(pItem->text(nCol), + QClipboard::Clipboard); +} + +/** + * Hides all items in the page that do not meet the given search criteria. + * This slot is connected to the search() signal of the QueryResultsMenu + * object. + * The search is incremental: only visible items are checked, so that a new + * search goes over the results of the previous one. + * @param nCol The list column to search in + */ +void QueryView::slotFilter(int nCol) +{ + SearchResultsDlg dlg(this); + QRegExp re; + QListViewItem* pItem; + bool bNegate; + + // Prepare the dialogue + dlg.setColumn(nCol); + + // Show the dialogue + if (dlg.exec() != QDialog::Accepted) + return; + + // Get the selected regular expression + dlg.getPattern(re); + bNegate = dlg.isNegated(); + + // Disable visual updates while search is in progress + setUpdatesEnabled(false); + + // Iterate over all items in the list + pItem = firstChild(); + while (pItem != NULL) { + // Filter visible items only + if (pItem->isVisible() && + (re.search(pItem->text(nCol)) == -1) != bNegate) { + pItem->setVisible(false); + } + + pItem = pItem->nextSibling(); + } + + // Redraw the list + setUpdatesEnabled(true); + triggerUpdate(); +} + +/** + * Makes all list items visible. + * This slot is connected to the showAll() signal of the QueryResultsMenu + * object. + */ +void QueryView::slotShowAll() +{ + QListViewItem* pItem; + + // Iterate over all items in the list + pItem = firstChild(); + while (pItem != NULL) { + pItem->setVisible(true); + pItem = pItem->nextSibling(); + } +} + +/** + * Deletes the item on which a popup-menu has been invoked. + * This slot is connected to the remove() signal of the QueryResultsMenu + * object. + * @param pItem The item to remove + */ +void QueryView::slotRemoveItem(QListViewItem* pItem) +{ + delete pItem; +} + +/** + * Moves the iterator to the next _visible_ item in the list. + */ +void QueryView::Iterator::next() +{ + if (m_pItem == NULL) + return; + + do { + m_pItem = m_pItem->nextSibling(); + } while (m_pItem != NULL && !m_pItem->isVisible()); +} + +/** + * @return The function associated with the list item the iterator points to + */ +QString QueryView::Iterator::getFunc() +{ + if (m_pItem == NULL) + return ""; + + return m_pItem->text(0); +} + +/** + * @return The file associated with the list item the iterator points to + */ +QString QueryView::Iterator::getFile() +{ + if (m_pItem == NULL) + return ""; + + return m_pItem->text(1); +} + +/** + * @return The line number associated with the list item the iterator points + * to + */ +QString QueryView::Iterator::getLine() +{ + if (m_pItem == NULL) + return ""; + + return m_pItem->text(2); +} + +/** + * @return The text associated with the list item the iterator points to + */ +QString QueryView::Iterator::getText() +{ + if (m_pItem == NULL) + return ""; + + return m_pItem->text(3); +} + +#include "queryview.moc" diff --git a/src/queryview.h b/src/queryview.h new file mode 100644 index 0000000..c896d6b --- /dev/null +++ b/src/queryview.h @@ -0,0 +1,217 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef QUERYVIEW_H +#define QUERYVIEW_H + +#include <qlistview.h> +#include <qregexp.h> + +class QueryResultsMenu; + +/** + * Items in a query view. + * The sole purpose for creating a new class is to be able to sort query + * results numerically by line number. + * @author Elad Lahav + */ +class QueryViewItem : public QListViewItem +{ +public: + /** + * Class constructor. + * Used for list views. + * @param pView The view widget + * @param pAfter The item to preceed the new one + * @param nLineCol The index of the line column + */ + QueryViewItem(QListView* pView, QListViewItem* pAfter, + int nLineCol) : QListViewItem(pView, pAfter), m_nLineCol(nLineCol) + {} + + /** + * Class constructor. + * Used for tree views. + * @param pParent The parent item + * @param pAfter The item to preceed the new one + * @param nLineCol The index of the line column + */ + QueryViewItem(QListViewItem* pParent, QListViewItem* pAfter, + int nLineCol) : QListViewItem(pParent, pAfter), m_nLineCol(nLineCol) + {} + + /** + * Compares two items. + * If the given column holds line numbers, than the items are compared + * by their numeric values. Otherwise, a standard text comparison is + * performed. + * @param pItem The item to compare to + * @param nCol The column by which to compare + * @param bAscend Whether sorting in ascending or descending order + * @return 0 if the items are equal, 1 if the current item is greater, + * -1 if the current item is smaller + */ + virtual int compare(QListViewItem* pItem, int nCol, bool bAscend) const { + if (nCol == m_nLineCol) { + uint nLineCur, nLineOther; + int nResult; + + // Get the line numbers of each item + nLineCur = text(nCol).toUInt(); + nLineOther = pItem->text(nCol).toUInt(); + + // Compare the line numbers + nResult = nLineCur - nLineOther; + if (nResult == 0) + return 0; // Items are equal + else if (nResult > 0) + return 1; // The first item is greater + else + return -1; // The second item is greater + } + + return QListViewItem::compare(pItem, nCol, bAscend); + } + +private: + /** The index of the column holding the line numbers. */ + int m_nLineCol; +}; + +/** + * A list view widget for displaying locations in the source code. Each record + * (list item) represents a single line of code. + * The main purpose of this class is for showing query results (@see + * QueryViewDriver), but is can also serve as a base class for any widget + * which needs to refer to locations in the source code (@see + * HistoryView). + * The view has 4 columns, for showing the file path, function name, line + * number and line text of a code location. + * The widget owns a popup menu which allows users to copy information + * from records, filter records, and more. + * @author Elad Lahav + */ +class QueryView : public QListView +{ + Q_OBJECT + +public: + QueryView(QWidget* pParent = 0, const char* szName = 0); + ~QueryView(); + + virtual void addRecord(const QString&, const QString&, const QString&, + const QString&, QListViewItem* pParent = NULL); + virtual void select(QListViewItem*); + virtual void selectNext(); + virtual void selectPrev(); + virtual void queryProgress(); + virtual void queryFinished(uint, QListViewItem* pParent = NULL); + + /** + * Provides an iterator over the list of query results. + * @author Elad Lahav + */ + class Iterator + { + public: + /** + * Default constructor. + */ + Iterator() : m_pItem(NULL) {} + + /** + * Copy constructor. + * @param itr The copied object + */ + Iterator(const Iterator& itr) : m_pItem(itr.m_pItem) {} + + /** + * @return true if the iterator points _beyond_ the end of the list, + * false otherwise + */ + bool isEOF() { return m_pItem == NULL; } + + void next(); + + QString getFunc(); + QString getFile(); + QString getLine(); + QString getText(); + + private: + /** Points to the current list item. */ + QListViewItem* m_pItem; + + /** + * Private constructor used to return initialised iterators. + * This constructor can only be called from within QueryView. + * @param pItem The initial list item + */ + Iterator(QListViewItem* pItem) : m_pItem(pItem) {} + + friend class QueryView; + }; + + Iterator getIterator(); + +signals: + /** + * Notifies the owner widget that it needs to be visible since some + * information is available to display. + * This information may be an advancement of the progress bar, + * availability of query results, etc. + */ + void needToShow(); + + /** + * Emitted when a list record is selected. + * Selection is done by either double-clicking a query or by highlighting + * it and then pressing the ENTER key. + * @param sFile The "File" field of the selected record + * @param nLine The "Line" field of the selected record + */ + void lineRequested(const QString& sFile, uint nLine); + +protected: + /** A popup-menu for manipulating query result items. */ + QueryResultsMenu* m_pQueryMenu; + + /** A pointer to the last item (used for appending results). */ + QListViewItem* m_pLastItem; + + void contentsMouseDoubleClickEvent(QMouseEvent*); + +protected slots: + virtual void slotRecordSelected(QListViewItem*); + virtual void slotFindDef(const QString&); + virtual void slotCopy(QListViewItem*, int); + virtual void slotFilter(int); + virtual void slotShowAll(); + virtual void slotRemoveItem(QListViewItem*); +}; + +#endif diff --git a/src/queryviewdlg.cpp b/src/queryviewdlg.cpp new file mode 100644 index 0000000..93cd85e --- /dev/null +++ b/src/queryviewdlg.cpp @@ -0,0 +1,111 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include "queryviewdlg.h" +#include "queryviewdriver.h" + +/** + * Class constructor. + * @param nFlags Controls the behaviour of the diaogue + * @param pParent The parent widget + * @param szName The widget's name + */ +QueryViewDlg::QueryViewDlg(uint nFlags, QWidget* pParent, + const char* szName) : + QueryViewLayout(pParent, szName), + m_nFlags(nFlags) +{ + // Set the destructive flag, if required + if (nFlags & DestroyOnClose) + setWFlags(getWFlags() | WDestructiveClose); + + // Create a driver for running queries + m_pDriver = new QueryViewDriver(m_pView, this); + + // Show the dialogue when instructed by the driver + connect(m_pView, SIGNAL(needToShow()), this, SLOT(slotShow())); + + // Propagate the lineRequested() signal from the QueryView object + connect(m_pView, SIGNAL(lineRequested(const QString&, uint)), this, + SLOT(slotLineRequested(const QString&, uint))); + + // Make the dialogue modal + setModal(true); +} + +/** + * Class destructor. + */ +QueryViewDlg::~QueryViewDlg() +{ +} + +/** + * Starts a query. + * @param nType The type of the query + * @param sText The query's text + * @param bCase true for case-sensitive queries, false otherwise + */ +void QueryViewDlg::query(uint nType, const QString& sText, bool bCase) +{ + m_pDriver->query(nType, sText, bCase); +} + +/** + * Make the dialogue visible when instructed by the driver. + * This slot is connected to the show() signal emitted by the QueryViewDriver + * object. + */ +void QueryViewDlg::slotShow() +{ + show(); +} + +/** + * Propagates the lineRequested() signal from the view object. + * If the CloseOnSelect flag is set, the dialogue is closed. + * This slot is connected to the lineRequested() signal emitted by the + * QueryView widget. + */ +void QueryViewDlg::slotLineRequested(const QString& sFileName, uint nLine) +{ + emit lineRequested(sFileName, nLine); + + if (m_nFlags & CloseOnSelect) + close(); +} + +/** + * @return A QueryView iterator initialised to the beginning of the result + * list + */ +QueryView::Iterator QueryViewDlg::getIterator() +{ + return m_pView->getIterator(); +} + +#include "queryviewdlg.moc" diff --git a/src/queryviewdlg.h b/src/queryviewdlg.h new file mode 100644 index 0000000..fc1c2f7 --- /dev/null +++ b/src/queryviewdlg.h @@ -0,0 +1,88 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef QUERYVIEWDLG_H +#define QUERYVIEWDLG_H + +#include "queryviewlayout.h" +#include "queryview.h" + +class QueryViewDriver; + +/** + * A dialogue for running and displaying queries. + * The dialogue is built around a QueryView widget, and uses a QueryViewDriver + * object to run a query. The dialogue is used in different contexts, such + * as executing quick definitions, displaying multiple call nodes in a graph, + * etc. + * The dialogue is always modal, but should not be launched using exec(). + * Instead, it is created as a modeless, hidden, dialogue. It then becomes + * modal (and visible) only if and when information is available (@see + * QueryViewDriver::show()). + * @author Elad Lahav + */ +class QueryViewDlg : public QueryViewLayout +{ + Q_OBJECT + +public: + QueryViewDlg(uint nFlags = 0, QWidget* pParent = 0, + const char* szName = 0); + ~QueryViewDlg(); + + /** These flags control the behaviour of the dialogue. */ + enum { CloseOnSelect = 0x1, DestroyOnClose = 0x2, + DestroyOnSelect = CloseOnSelect | DestroyOnClose }; + + void query(uint, const QString&, bool bCase = true); + + QueryView::Iterator getIterator(); + +signals: + /** + * Propagates the lineRequested() signal of the embedded QueryView + * widget. + * @param sFile The "File" field of the selected record + * @param nLine The "Line" field of the selected record + */ + void lineRequested(const QString& sFile, uint nLine); + +private: + /** Flags the control the behaviour of the dialogue. */ + uint m_nFlags; + + /** Used for running Cscope queries and displaying their results in the + view. */ + QueryViewDriver* m_pDriver; + +private slots: + void slotShow(); + void slotLineRequested(const QString&, uint); +}; + +#endif + diff --git a/src/queryviewdriver.cpp b/src/queryviewdriver.cpp new file mode 100644 index 0000000..f04c44d --- /dev/null +++ b/src/queryviewdriver.cpp @@ -0,0 +1,180 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <klocale.h> +#include "queryviewdriver.h" +#include "queryview.h" + +/** + * Class constructor. + * Creates a driver that adds new records as root items in the given view. + * @param pView The view to which this driver should add records + * @param pParent The parent object of the driver + * @param szName The name of the object + */ +QueryViewDriver::QueryViewDriver(QueryView* pView, QObject* pParent, + const char* szName) : QObject(pParent, szName), + m_pView(pView), + m_pItem(NULL), + m_progress(pView), + m_bRunning(false) +{ + m_pCscope = new CscopeFrontend(); + + // Add records to the page when Cscope outputs them + connect(m_pCscope, SIGNAL(dataReady(FrontendToken*)), this, + SLOT(slotDataReady(FrontendToken*))); + + // Report progress information + connect(m_pCscope, SIGNAL(progress(int, int)), this, + SLOT(slotProgress(int, int))); + + // Perform tasks when the query process terminates + connect(m_pCscope, SIGNAL(finished(uint)), this, + SLOT(slotFinished(uint))); + + connect(m_pView, SIGNAL(destroyed()), this, SLOT(slotViewClosed())); +} + +/** + * Class destructor. + */ +QueryViewDriver::~QueryViewDriver() +{ + delete m_pCscope; +} + +/** + * Executes a query. + * @param nType The type of the query (@see CscopeFrontend) + * @param sText The query's text + * @param bCase true for case-sensitive queries, false otherwise + * @param pItem If non-null, represents an view item passed back to + * the view in a call to addRecord() + */ +void QueryViewDriver::query(uint nType, const QString& sText, bool bCase, + QListViewItem* pItem) +{ + m_pItem = pItem; + + // Make sure sorting is disabled while entries are added + m_pView->setSorting(100); + + // Execute the query + m_pCscope->query(nType, sText, bCase); + m_bRunning = true; + m_pView->setEnabled(false); + m_pView->setUpdatesEnabled(false); +} + +/** + * Adds a query entry to the view. + * Called by a CscopeFrontend object, when a new entry was received in its + * whole from the Cscope back-end process. + * @param pToken The first token in the entry + */ +void QueryViewDriver::slotDataReady(FrontendToken* pToken) +{ + QString sFile, sFunc, sLine, sText; + + // Get the file name + sFile = pToken->getData(); + pToken = pToken->getNext(); + + // Get the function name + sFunc = pToken->getData(); + pToken = pToken->getNext(); + + // Get the line number + sLine = pToken->getData(); + if (!sLine.toInt()) { + // Line number could not be 0! + // means that function name was empty + sLine = sFunc; + sFunc = "<global>"; + } + else { + pToken = pToken->getNext(); + } + + // Get the line's text + sText = pToken->getData(); + pToken = pToken->getNext(); + + // Add a new item at the end of the list + m_pView->addRecord(sFunc, sFile, sLine, sText, m_pItem); +} + +/** + * Handles a finished query event, reported by the Cscope frontend object. + * If no resutls are available, a proper message is displayed. If only one + * record was generated by Cscope, it is automatically selected for viewing. + * @param nRecords The number of records the query has generated + */ +void QueryViewDriver::slotFinished(uint nRecords) +{ + // The query is no longer running + m_bRunning = false; + m_pView->setEnabled(true); + m_pView->setUpdatesEnabled(true); + m_pView->triggerUpdate(); + + // Destroy the progress bar + m_progress.finished(); + + // Let owner widget decide what to do based on the number of records + m_pView->queryFinished(nRecords, m_pItem); +} + +/** + * Displays search progress information. + * This slot is connected to the progress() signal emitted by a + * CscopeFrontend object. + * @param nFiles The number of files scanned + * @param nTotal The total number of files in the project + */ +void QueryViewDriver::slotProgress(int nFiles, int nTotal) +{ + // A progress report is available, instruct the owner object to show the + // view + if (nTotal > 1) + m_pView->queryProgress(); + + // Set the progress bar + m_progress.setProgress(nFiles, nTotal); +} + +/** + * Called when the owner view is destroyed. + */ +void QueryViewDriver::slotViewClosed() +{ + m_pView = NULL; + m_pCscope->kill(); +} + +#include "queryviewdriver.moc" diff --git a/src/queryviewdriver.h b/src/queryviewdriver.h new file mode 100644 index 0000000..77ff9ed --- /dev/null +++ b/src/queryviewdriver.h @@ -0,0 +1,84 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef QUERYVIEWDRIVER_H +#define QUERYVIEWDRIVER_H + +#include <qobject.h> +#include <qlistview.h> +#include "cscopefrontend.h" + +class QueryView; + +/** + * Executes a Cscope query and displays the results in a QueryView widget. + * This class is used in conjunction with QueryView to create a query + * display object. The driver uses the view widget to display result records + * of an executed query. It also uses the view as a parent widget for the + * query progress bar. + * @author Elad Lahav + */ +class QueryViewDriver : public QObject +{ + Q_OBJECT + +public: + QueryViewDriver(QueryView*, QObject* pParent = 0, const char* szName = 0); + ~QueryViewDriver(); + + void query(uint, const QString&, bool bCase, QListViewItem* pItem = NULL); + + /** + * @return true if a query is currently running, false otherwise + */ + bool isRunning() { return m_bRunning; } + +private: + /** Cscope object for running queries. */ + CscopeFrontend* m_pCscope; + + /** The view to which this object adds result records. */ + QueryView* m_pView; + + /** QueryView item passed to addRecord(). */ + QListViewItem* m_pItem; + + /** Displays query progress information. */ + CscopeProgress m_progress; + + /** This flag is set to true when a query is executed, and back to false + when the the CscopeFrontend object emits the finished() signal. */ + bool m_bRunning; + +private slots: + void slotDataReady(FrontendToken*); + void slotFinished(uint); + void slotProgress(int, int); + void slotViewClosed(); +}; + +#endif diff --git a/src/queryviewlayout.ui b/src/queryviewlayout.ui new file mode 100644 index 0000000..ae097c5 --- /dev/null +++ b/src/queryviewlayout.ui @@ -0,0 +1,167 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>QueryViewLayout</class> +<widget class="QDialog"> + <property name="name"> + <cstring>QueryViewLayout</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>654</width> + <height>499</height> + </rect> + </property> + <property name="caption"> + <string>Query Results</string> + </property> + <property name="sizeGripEnabled"> + <bool>true</bool> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QueryView"> + <property name="name"> + <cstring>m_pView</cstring> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Right-click inside the list for more options.</string> + </property> + </widget> + <widget class="Line"> + <property name="name"> + <cstring>line1</cstring> + </property> + <property name="frameShape"> + <enum>HLine</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout1</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <spacer> + <property name="name"> + <cstring>Horizontal Spacing2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>buttonOk</cstring> + </property> + <property name="text"> + <string>&OK</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="autoDefault"> + <bool>true</bool> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>buttonCancel</cstring> + </property> + <property name="text"> + <string>&Cancel</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="autoDefault"> + <bool>true</bool> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<customwidgets> + <customwidget> + <class>QueryView</class> + <header location="local">queryview.h</header> + <sizehint> + <width>-1</width> + <height>-1</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>5</hordata> + <verdata>5</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + </customwidget> +</customwidgets> +<images> + <image name="image0"> + <data format="PNG" length="1003">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b000003b249444154388db5944d4c5c5518869f73ce9db9cc40f929cc30300e18129a50685268a28291982e1a2175212eaab1c49d3f8971e7aec6aedb54a32b435dd8685cb8c0c4b8b0feb421b7bd8186a069d23209144b18cb0c02f3732ff3c3ccbdd705a571941213f1dd9d93739ef37e6fbeef08d334d9d5d0d090c701c8344d2176c1a6697a5d5d5dd8b64d2a95c2b2ac7f05b12c8b783c8e6118d8b6fd685fdb753a39394928e2a7c55724d693a7e214104220242805520a84d8b9e4790f0b1302c7751958f1e30534be9fa41a0c60db362dbe227f64af91b6e7719c2dfc0117a71c603da591de2882a7d3d4ec27122b10acad50aeb88044d542b84de3af7ab44aa55274f4e449dbf354bc347aa096fbf37e7efa2ecbad1b36907d78b289de8120232f37726250c3f52cf0a026a0f6065b9645d929e038167aa096d91b307171059034b5f8e9e9eb44fa052b4b25eeccd9dc99cb71f6ad082fbea2e351c075bdbdc13b9109fc01b83fef63e2e232e03076b68393231a0dcd024daf502c1e66e67a89cb971ef0e5a74bb4b61e6378248094b92ab0ac024b70ca017ef8360394187b2dc6d8b89ffa500e4d4f93d9083275d5e6f9913ade3dd70ee87c7d25c95646a25415aadab152b09ed4f8c5cc130a853839eac3951b684a61e70ef3f9c739668c24eba912afbfddc6d4d506e6a6d7b8b7d082aa8eb8dab19482cdcd220e36dd3dc19df23545de0a71f952861923493456cfd3cf36123c54a0b7bf0e8064621b21f7712c040857071ca4eea0f40a99f510573ec971cb48030e811a1faded3e94be85f2fb00703d1ff26f96ab9f011a5b04d0c4f26fdb948a754c1b25668c2491483d5d5d611617d6b8f0fe32cb779b492ce65168b4c774a4701eefd8f3a02d56e6e8f13aeefe9a63fa5a89d1970e91d908f3d46003e1480d173e28b37827c9b977348ab92ce16890237d1ec907ee3e8e3d8f60d0e1f4583d009f7d9860662acff89bad1c1faad0717493f7ce7713ed8cb0995c255f2a317a26427b67198f6a70f51c0a41b902279ed1187f23cc17130b7c74de65eac77a7a8f05517e41e2deefa4930576db60fa7a8ee1535134df3e93e7b82e0817476439fd6a9070a49f6fbe4a3077739db99b15c001146dd13a46cf1cc1f83943c62e51de76b0738fc9381e8f3390f0236b252e124999e11724fd833196164bacadb87848224fe874f779b43fe9f2dca928956d505a96dbb3f9bdc18661e00534c26d1a7a40e13912a11c9454280da41048a9915c75585df500074d53d816dc9edd229528ef0db66dbbea3ffdaffa471f1f28d8344df1bf800f1a6e9aa6f813c39885bc050f269c0000000049454e44ae426082</data> + </image> +</images> +<connections> + <connection> + <sender>buttonOk</sender> + <signal>clicked()</signal> + <receiver>QueryViewLayout</receiver> + <slot>accept()</slot> + </connection> + <connection> + <sender>buttonCancel</sender> + <signal>clicked()</signal> + <receiver>QueryViewLayout</receiver> + <slot>reject()</slot> + </connection> +</connections> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>queryview.h</includehint> +</includehints> +</UI> diff --git a/src/querywidget.cpp b/src/querywidget.cpp new file mode 100644 index 0000000..113f216 --- /dev/null +++ b/src/querywidget.cpp @@ -0,0 +1,601 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qtoolbutton.h> +#include <qtooltip.h> +#include <klocale.h> +#include <kmessagebox.h> +#include "querywidget.h" +#include "kscopepixmaps.h" +#include "kscopeconfig.h" + +/** + * Class constructor. + * @param pParent The parent widget + * @param szName The widget's name + */ +QueryWidget::QueryWidget(QWidget* pParent, const char* szName) : + QueryWidgetLayout(pParent, szName), + m_pPageMenu(NULL), + m_pLockAction(NULL), + m_pHistPage(NULL), + m_bHistEnabled(true), + m_nQueryPages(0) +{ + // Pages can be closed by clicking their tabs + m_pQueryTabs->setHoverCloseButton(true); + + // Change the lock action state according to the current page + connect(m_pQueryTabs, SIGNAL(currentChanged(QWidget*)), this, + SLOT(slotCurrentChanged(QWidget*))); + + // Close a query when its tab button is clicked + connect(m_pQueryTabs, SIGNAL(closeRequest(QWidget*)), this, + SLOT(slotClosePage(QWidget*))); + + // Show the menu when requested + connect(m_pQueryTabs, SIGNAL(contextMenu(const QPoint&)), this, + SLOT(slotContextMenu(const QPoint&))); + connect(m_pQueryTabs, SIGNAL(contextMenu(QWidget*, const QPoint&)), this, + SLOT(slotContextMenu(QWidget*, const QPoint&))); +} + +/** + * Class destructor. + */ +QueryWidget::~QueryWidget() +{ +} + +/** + * Runs a query in a query page. + * A query page is first selected, with a new one created if required. The + * method then creates a Cscope process and runs the query. + * @param nType The query's numeric type code + * @param sText The query's text, as entered by the user + * @param bCase true for case-sensitive queries, false otherwise + */ +void QueryWidget::initQuery(uint nType, const QString& sText, bool bCase) +{ + QueryPage* pPage; + + // Make sure we have a query page + findQueryPage(); + pPage = (QueryPage*)currentPage(); + + // Use the current page, or a new page if the current one is locked + if (pPage->isLocked()) { + addQueryPage(); + pPage = (QueryPage*)currentPage(); + } + + // Reset the page's results list + pPage->clear(); + pPage->query(nType, sText, bCase); + + // Set the page's tab text according to the new query + setPageCaption(pPage); +} + +/** + * Applies the user's colour and font preferences to all pages. + */ +void QueryWidget::applyPrefs() +{ + QueryPage* pPage; + int nPages, i; + + // Iterate query pages + nPages = m_pQueryTabs->count(); + for (i = 0; i < nPages; i++) { + pPage = (QueryPage*)m_pQueryTabs->page(i); + pPage->applyPrefs(); + setPageCaption(pPage); + } +} + +/** + * Loads all pages saved when the project was closed. + * @param sProjPath The full path of the project directory + * @param slFiles The list of query file names to load + */ +void QueryWidget::loadPages(const QString& sProjPath, + const QStringList& slFiles) +{ + QStringList::ConstIterator itr; + QueryPageBase* pPage; + QString sName; + + // Iterate through query files + for (itr = slFiles.begin(); itr != slFiles.end(); ++itr) { + // Set the target page, based on the file type (query or history) + if ((*itr).startsWith("History")) { + findHistoryPage(); + pPage = m_pHistPage; + } + else { + findQueryPage(); + pPage = (QueryPage*)currentPage(); + } + + // Load a query file to this page, and lock the page + if (pPage->load(sProjPath, *itr)) { + setPageCaption(pPage); + setPageLocked(pPage, true); + } + } +} + +/** + * Stores all pages marked for saving into files in the project directory. + * @param sProjPath The full path of the project directory + * @param slFiles Holds a list of query file names, upon return + */ +void QueryWidget::savePages(const QString& sProjPath, QStringList& slFiles) +{ + int nPageCount, i; + QueryPage* pPage; + QString sFileName; + + // Iterate pages + nPageCount = m_pQueryTabs->count(); + for (i = 0; i < nPageCount; i++) { + pPage = (QueryPage*)m_pQueryTabs->page(i); + if (pPage->shouldSave()) { + // Store this query page + if (pPage->save(sProjPath, sFileName) && !sFileName.isEmpty()) + slFiles.append(sFileName); + } + } +} + +/** + * Adds a new position record to the active history page. + * @param sFile The file path + * @param nLine The line number + * @param sText The contents of the line pointed to by the file path and + * line number + */ +void QueryWidget::addHistoryRecord(const QString& sFile, uint nLine, + const QString& sText) +{ + // Validate file name and line number + if (sFile.isEmpty() || nLine == 0) + return; + + // Do nothing if history logging is disabled + if (!m_bHistEnabled) + return; + + // Make sure there is an active history page + findHistoryPage(); + + // Add the position entry to the active page + m_pHistPage->addRecord(sFile, nLine, sText); +} + +/** + * Sets the tab caption and tool-tip for the given page. + * @param pPage The page whose tab needs to be changed + */ +void QueryWidget::setPageCaption(QueryPageBase* pPage) +{ + m_pQueryTabs->changeTab(pPage, + pPage->getCaption(Config().getUseBriefQueryCaptions())); + m_pQueryTabs->setTabToolTip(pPage, pPage->getCaption()); +} + +/** + * Creates a new query page, and adds it to the tab widget. + * The new page is set as the current one. + */ +void QueryWidget::addQueryPage() +{ + QueryPage* pPage; + QString sTitle; + + // Create the page + pPage = new QueryPage(this); + + // Add the page, and set it as the current one + m_pQueryTabs->insertTab(pPage, GET_PIXMAP(TabUnlocked), "Query", + m_nQueryPages++); + setCurrentPage(pPage); + + // Emit the lineRequested() signal when a query record is selected on + // this page + connect(pPage, SIGNAL(lineRequested(const QString&, uint)), this, + SLOT(slotRequestLine(const QString&, uint))); +} + +/** + * Creates a new query page, and emits signal about it. + */ +void QueryWidget::slotNewQueryPage() +{ + addQueryPage(); + emit newQuery(); +} + +/** + * Locks or unlocks a query. + * This slot is connected to the toggled() signal of the lock query button. + * @param bOn true if the new state of the button is "on", false if it is + * "off" + */ +void QueryWidget::slotLockCurrent(bool bOn) +{ + QueryPageBase* pPage; + + pPage = currentPage(); + + if (pPage != NULL) + setPageLocked(currentPage(), bOn); +} + +/** + * Locks or unlocks a query, by toggling the current state. + */ +void QueryWidget::slotLockCurrent() +{ + QueryPageBase* pPage; + + pPage = currentPage(); + if (pPage != NULL) + setPageLocked(pPage, !pPage->isLocked()); +} + +/** + * Reruns the query whose results are displayed in the current page. + */ +void QueryWidget::slotRefreshCurrent() +{ + QueryPage* pPage; + + // Make sure the current page is a valid, non-empty one + pPage = dynamic_cast<QueryPage*>(currentPage()); + if (pPage == NULL) + return; + + // Clear the current page contents + pPage->refresh(); +} + +/** + * Selects the next query result record in the current query page. + */ +void QueryWidget::slotNextResult() +{ + QueryPage* pPage; + + // Select the next record in the current page + pPage = dynamic_cast<QueryPage*>(currentPage()); + if (pPage != NULL) + pPage->selectNext(); +} + +/** + * Selects the next query result record in the current query page. + */ +void QueryWidget::slotPrevResult() +{ + QueryPage* pPage; + + // Select the next record in the current page + pPage = dynamic_cast<QueryPage*>(currentPage()); + if (pPage != NULL) + pPage->selectPrev(); +} + +/** + * Closes the current query page. + */ +void QueryWidget::slotCloseCurrent() +{ + QWidget* pPage; + + // Close the current page + pPage = currentPage(); + if (pPage != NULL) + slotClosePage(pPage); +} + +/** + * Closes all query pages. + */ +void QueryWidget::slotCloseAll() +{ + int nPageCount, i; + QueryPage* pPage; + + // Close all pages + nPageCount = m_pQueryTabs->count(); + for (i = 0; i < nPageCount; i++) { + pPage = (QueryPage*)m_pQueryTabs->page(0); + m_pQueryTabs->removePage(pPage); + delete pPage; + } + + m_pHistPage = NULL; +} + +/** + * Handles the "Go->Back" menu command. + * Moves to the previous position in the position history. + */ +void QueryWidget::slotHistoryPrev() +{ + if (m_pHistPage != NULL) { + m_bHistEnabled = false; + m_pHistPage->selectPrev(); + m_bHistEnabled = true; + } +} + +/** + * Handles the "Go->Forward" menu command. + * Moves to the next position in the position history. + */ +void QueryWidget::slotHistoryNext() +{ + if (m_pHistPage != NULL) { + m_bHistEnabled = false; + m_pHistPage->selectNext(); + m_bHistEnabled = true; + } +} + +/** + * Sets the active history page, if any, as the current page. + */ +void QueryWidget::selectActiveHistory() +{ + if (m_pHistPage) + setCurrentPage(m_pHistPage); +} + +/** + * Attaches the page operations menu to this widget. + * The page menu is a popup menu that handles such operations as opening a + * new page, closing a page, locking a page, etc. + * @param pMenu Pointer to the popup menu + * @param pAction Pointer to the "Lock/Unlock" toggle action + */ +void QueryWidget::setPageMenu(QPopupMenu* pMenu, KToggleAction* pAction) +{ + m_pPageMenu = pMenu; + m_pLockAction = pAction; +} + +/** + * Emits a signal indicating a certain source file and line number are + * requested. + * This slot is connected to the recordSelected() signal emitted by any of + * the query pages. The signal emitted by this slot is used to display an + * editor page at the requested line. + * @param sFileName The file's path + * @param nLine The requested line in the file + */ +void QueryWidget::slotRequestLine(const QString& sFileName, uint nLine) +{ + // Disable history if the request came from the active history page + if (currentPage() == m_pHistPage) + m_bHistEnabled = false; + + // Emit the signal + emit lineRequested(sFileName, nLine); + + // Re-enable history + if (currentPage() == m_pHistPage) + m_bHistEnabled = true; +} + +/** + * Update the lock button when the current query page changes. + * @param pWidget The new current page + */ +void QueryWidget::slotCurrentChanged(QWidget* pWidget) +{ + QueryPage* pPage; + + pPage = (QueryPage*)pWidget; + m_pLockAction->setChecked(pPage->isLocked()); +} + +/** + * Removes the given page from the tab widget. + * This slot is connected to the closeRequest() signal of the KTabBar object. + * @param pPage The page to close + */ +void QueryWidget::slotClosePage(QWidget* pPage) +{ + // Prompt the user before closing a locked query + if (((QueryPage*)pPage)->isLocked()) { + if (KMessageBox::questionYesNo(NULL, i18n("You about about to close" + " a locked page.\nAre you sure?")) == KMessageBox::No) { + return; + } + } + + // Check if the closed page is the active history page + if (pPage == m_pHistPage) + m_pHistPage = NULL; + // Update the number of open history pages + else if (dynamic_cast<HistoryPage*>(pPage) == NULL) + m_nQueryPages--; + + // Remove the page and delete the object + m_pQueryTabs->removePage(pPage); + delete pPage; +} + +/** + * Displays a context menu for page operations. + * This slot is connected to the contextMenu() signal, emitted by + * m_pQueryTabs. + * NOTE: We assume that the first item in the menu is "New". + * @param pt The point over which the mouse was clicked in request for the + * context menu + */ +void QueryWidget::slotContextMenu(const QPoint& pt) +{ + uint i; + + // Disable everything but the "new" action (clicked outside any widget) + for (i = 1; i < m_pPageMenu->count(); i++) + m_pPageMenu->setItemEnabled(m_pPageMenu->idAt(i), false); + + // Show the menu + m_pPageMenu->popup(pt); +} + +/** + * Displays a context menu for page operations. + * This slot is connected to the contextMenu() signal, emitted by + * m_pQueryTabs. + * @param pWidget The page under the mouse + * @param pt The point over which the mouse was clicked in request for + * the context menu + */ +void QueryWidget::slotContextMenu(QWidget* pWidget, const QPoint& pt) +{ + uint i; + + // Operations are on the current page, so we must ensure the clicked + // tab becomes the current one + setCurrentPage(pWidget); + + // Enable all operations + for (i = 1; i < m_pPageMenu->count(); i++) + m_pPageMenu->setItemEnabled(m_pPageMenu->idAt(i), true); + + // Show the menu + m_pPageMenu->popup(pt); +} + +/** + * Locks/unlocks the give page. + * @param pPage The page to lock or unlock + * @param bLock true to lock the page, false to unlock it + */ +void QueryWidget::setPageLocked(QueryPageBase* pPage, bool bLock) +{ + if (!pPage->canLock()) + return; + + // Set the locking state of the current page + pPage->setLocked(bLock); + m_pQueryTabs->setTabIconSet(pPage, bLock ? GET_PIXMAP(TabLocked) : + GET_PIXMAP(TabUnlocked)); + + // There can only be one unlocked history page. Check if a non-active + // query page is being unlocked + if (isHistoryPage(pPage) && (pPage != m_pHistPage) && !bLock) { + // Lock the active history page (may be NULL) + if (m_pHistPage != NULL) + setPageLocked(m_pHistPage, true); + + // Set the unlock page as the new active history page + m_pHistPage = (HistoryPage*)pPage; + } +} + +/** + * Ensures the current page is a query page that is ready to accept new + * queries. + * The function first checks the current page. If it is an unlocked query + * page, then nothing needs to be done. Otherwise, it checks for the first + * unlocked query page by iterating over all pages in the tab widget. If this + * fails as well, a new query page is created. + */ +void QueryWidget::findQueryPage() +{ + QueryPage* pPage; + int nPages, i; + + // First check if the current page is an unlocked query page + pPage = dynamic_cast<QueryPage*>(currentPage()); + if (pPage != NULL) { + if (!pPage->isLocked() && !pPage->isRunning()) + return; + } + + // Look for the first unlocked query page + nPages = m_pQueryTabs->count(); + for (i = 0; i < nPages; i++) { + pPage = dynamic_cast<QueryPage*>(m_pQueryTabs->page(i)); + if (pPage != NULL) { + if (!pPage->isLocked() && !pPage->isRunning()) { + setCurrentPage(pPage); + return; + } + } + } + + // Couldn't find an unlocked query page, create a new one + addQueryPage(); +} + +/** + * Ensures an active history page exists. + * The active history page is the only unlocked history page. If one does not + * exist, it is created. + */ +void QueryWidget::findHistoryPage() +{ + HistoryPage* pPage; + int nPages, i; + QString sTitle; + + // First check if the active history page is unlocked + if (m_pHistPage != NULL && !m_pHistPage->isLocked()) + return; + + // Look for the first unlocked history page + nPages = m_pQueryTabs->count(); + for (i = 0; i < nPages; i++) { + pPage = dynamic_cast<HistoryPage*>(m_pQueryTabs->page(i)); + if (pPage != NULL && !pPage->isLocked()) { + m_pHistPage = pPage; + return; + } + } + + // Couldn't find an unlocked query page, create a new one + m_pHistPage = new HistoryPage(this); + + // Add the page, and set it as the current one + m_pQueryTabs->insertTab(m_pHistPage, GET_PIXMAP(TabUnlocked), ""); + setPageCaption(m_pHistPage); + + // Emit the lineRequested() signal when a query record is selected on + // this page + connect(m_pHistPage, SIGNAL(lineRequested(const QString&, uint)), this, + SLOT(slotRequestLine(const QString&, uint))); +} + +#include "querywidget.moc" diff --git a/src/querywidget.h b/src/querywidget.h new file mode 100644 index 0000000..a798ec0 --- /dev/null +++ b/src/querywidget.h @@ -0,0 +1,152 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef QUERYWIDGET_H +#define QUERYWIDGET_H + +#include <qlistview.h> +#include <qpopupmenu.h> +#include <kaction.h> +#include "querywidgetlayout.h" +#include "tabwidget.h" +#include "querypage.h" +#include "historypage.h" +#include "projectmanager.h" + +/** + * A tabbed-window holding Cscope query results pages. + * @author Elad Lahav + */ + +class QueryWidget : public QueryWidgetLayout +{ + Q_OBJECT + +public: + QueryWidget(QWidget* pParent = 0, const char* szName = 0); + ~QueryWidget(); + + void addQueryPage(); + void initQuery(uint, const QString&, bool); + void applyPrefs(); + void loadPages(const QString&, const QStringList&); + void savePages(const QString&, QStringList&); + void addHistoryRecord(const QString&, uint, const QString&); + void selectActiveHistory(); + void setPageMenu(QPopupMenu*, KToggleAction*); + void getBookmarks(FileLocationList&); + + /** + * Enables/disables new history items. + * @param bEnabled true to enable new history items, false to + * disable + */ + void setHistoryEnabled(bool bEnabled) { m_bHistEnabled = bEnabled; } + +public slots: + void slotNewQueryPage(); + void slotLockCurrent(bool); + void slotLockCurrent(); + void slotRefreshCurrent(); + void slotNextResult(); + void slotPrevResult(); + void slotCloseCurrent(); + void slotCloseAll(); + void slotHistoryPrev(); + void slotHistoryNext(); + +signals: + /** + * Emitted when the a lineRequested() signal is received from any of the + * currently open query pages. + * @param sPath The full path of the requested source file + * @param nLine The requested line number + */ + void lineRequested(const QString& sPath, uint nLine); + + /** + * Emitted when new query page is requested by user + */ + void newQuery(); + +private: + /** A popup menu with query page commands (new query, lock/unlock, close + query, etc.). */ + QPopupMenu* m_pPageMenu; + + /** A toggle-like action for changing the locked state of a query. */ + KToggleAction* m_pLockAction; + + /** The active history page. */ + HistoryPage* m_pHistPage; + + /** Determines whether history items should be added to the active + history page. */ + bool m_bHistEnabled; + + /** The number of query pages currently open. */ + int m_nQueryPages; + + void setPageCaption(QueryPageBase*); + + /** + * @return The active page in the tab widget + */ + inline QueryPageBase* currentPage() { + return (QueryPageBase*)m_pQueryTabs->currentPage(); + } + + /** + * @param pWidget A query page to set as the current one + */ + inline void setCurrentPage(QWidget* pWidget) { + if (pWidget) + m_pQueryTabs->setCurrentPage(m_pQueryTabs->indexOf(pWidget)); + } + + /** + * Determines if a page is a history page. + * @param pPage The page to check + * @return true if the given page is a history page + */ + inline bool isHistoryPage(QWidget* pPage) { + return (dynamic_cast<HistoryPage*>(pPage) != NULL); + } + + void setPageLocked(QueryPageBase*, bool); + void findQueryPage(); + void findHistoryPage(); + +private slots: + void slotRequestLine(const QString&, uint); + void slotCurrentChanged(QWidget*); + void slotClosePage(QWidget*); + void slotContextMenu(const QPoint&); + void slotContextMenu(QWidget*, const QPoint&); +}; + +#endif diff --git a/src/querywidgetlayout.ui b/src/querywidgetlayout.ui new file mode 100644 index 0000000..52e7fb3 --- /dev/null +++ b/src/querywidgetlayout.ui @@ -0,0 +1,62 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>QueryWidgetLayout</class> +<widget class="QWidget"> + <property name="name"> + <cstring>QueryWidgetLayout</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>740</width> + <height>343</height> + </rect> + </property> + <property name="caption"> + <string>Form2</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <widget class="TabWidget"> + <property name="name"> + <cstring>m_pQueryTabs</cstring> + </property> + </widget> + </hbox> +</widget> +<customwidgets> + <customwidget> + <class>TabWidget</class> + <header location="local">tabwidget.h</header> + <sizehint> + <width>-1</width> + <height>-1</height> + </sizehint> + <container>1</container> + <sizepolicy> + <hordata>7</hordata> + <verdata>7</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + </customwidget> +</customwidgets> +<images> + <image name="image0"> + <data format="PNG" length="437">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b0000017c49444154388dad95db71c32010458f322e652966f3ef325031a68bf8df14c3f6e27c80c4338927e39df1182474b85c2ed2465f4fde53db76b4bcbf3e55af98414af79709c778b3815cfe9f8fc7576e39803b249afe1ff074e716c062bd76696716cd83a0519c2019dd444e66b8035ad12738a53ba274d0142146a6658a802a386d2ff6632e5d2fd5668c10430f3b56660621801a783fab9fc1add20255cdbf56d131698c45fd0ec49ef1d1f55c7df084fa19eab52a0d012c31d5a43859f554b5bf77dbcbbd62c1e17fb291322a86d367915ee90925a70707aeac70a578062faa854213b745ec7e061f2a0a6815b77dcf2a53d90b591ca2c96327d906b30c5505cae17192a118ec9ff5191508831d4b2b8e4d8b21abf23e5f1307b7db30d6b33cf6cb1cab56c5a1e4d53940eaf28f4a114c67bbbb77c5d926ab142939b5d967f5bdff63e2ceb79b08ecc332c5954db21a2971d9535c1fb331392718ca0691978cf16b9cda6169919c0efcce7ae980fca7b6a6fd56d5dbd07fdbc7f41bcb78aa0bdc5b1e190000000049454e44ae426082</data> + </image> +</images> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>tabwidget.h</includehint> +</includehints> +</UI> diff --git a/src/scanprogressdlg.cpp b/src/scanprogressdlg.cpp new file mode 100644 index 0000000..e380c60 --- /dev/null +++ b/src/scanprogressdlg.cpp @@ -0,0 +1,82 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qlabel.h> +#include <qpushbutton.h> +#include <klocale.h> +#include "scanprogressdlg.h" + +/** + * Class constructor. + * @param pParent The parent widget + * @param szName The widget's name + */ +ScanProgressDlg::ScanProgressDlg(QWidget* pParent, const char* szName) : + ScanProgressLayout(pParent, szName), + m_nFiles(0), + m_nCurFiles(0) +{ + show(); + + // Emit the cancelled() signal when the "Cancel" button is clicked + connect(m_pCancelButton, SIGNAL(clicked()), this, SIGNAL(cancelled())); +} + +/** + * Class destructor. + */ +ScanProgressDlg::~ScanProgressDlg() +{ +} + +/** + * Adds the given number of files to the total count of files already scanned. + * A visual indication of the progress is given in intervals of more than 100 + * files (to prevent too-frequent GUI updates.) + * @param nFiles The number of files scanned since the last call + */ +void ScanProgressDlg::addFiles(int nFiles) +{ + QString sText; + + // Do nothing if no files were scanned + if (nFiles <= 0) + return; + + // Update the total number of files scanned + m_nFiles += nFiles; + + // Update progress only if more than 100 files were scanned since the last + // update + if ((m_nFiles - m_nCurFiles) > 100) { + sText.sprintf(i18n("Scanned %d files..."), m_nFiles); + m_pText->setText(sText); + m_nCurFiles = m_nFiles; + } +} + +#include "scanprogressdlg.moc" diff --git a/src/scanprogressdlg.h b/src/scanprogressdlg.h new file mode 100644 index 0000000..84564de --- /dev/null +++ b/src/scanprogressdlg.h @@ -0,0 +1,69 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef SCANPROGRESSDLG_H +#define SCANPROGRESSDLG_H + +#include <qwidget.h> +#include <scanprogresslayout.h> + +/** + * Displays the progress of a file scan operation. + * This dialogue is displayed while a ProjectFilesDlg dialogue scans a + * directory tree for all files matching the patterns defined for the + * project's source files. + * @author Elad Lahav + */ + +class ScanProgressDlg : public ScanProgressLayout +{ + Q_OBJECT + +public: + ScanProgressDlg(QWidget* pParent = 0, const char* szName = 0); + ~ScanProgressDlg(); + + void addFiles(int); + +signals: + /** + * Indicates that the dialogue's "Cancel" button hsa been clicked by the + * user. + */ + void cancelled(); + +private: + /** The total number of files scanned thus far. */ + int m_nFiles; + + /** The number of files currently displayed in the progress report (which + may be smaller than m_nFiles since not every call to addFiles() updates + the progress display.)*/ + int m_nCurFiles; +}; + +#endif diff --git a/src/scanprogresslayout.ui b/src/scanprogresslayout.ui new file mode 100644 index 0000000..85482a8 --- /dev/null +++ b/src/scanprogresslayout.ui @@ -0,0 +1,115 @@ +<!DOCTYPE UI><UI version="3.1" stdsetdef="1"> +<class>ScanProgressLayout</class> +<widget class="QDialog"> + <property name="name"> + <cstring>ScanProgressLayout</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>198</width> + <height>103</height> + </rect> + </property> + <property name="caption"> + <string>Scanning Directory</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout4</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>m_pText</cstring> + </property> + <property name="text"> + <string>Scanned 0 files...</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout2</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pCancelButton</cstring> + </property> + <property name="text"> + <string>Cancel</string> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + </vbox> + </widget> + </vbox> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/src/searchlist.cpp b/src/searchlist.cpp new file mode 100644 index 0000000..ccff869 --- /dev/null +++ b/src/searchlist.cpp @@ -0,0 +1,270 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qheader.h> +#include "searchlist.h" + +/** + * Intercepting additional key events of QLineEdit to browse the list + * @param pKey The pressed key event + */ +void SearchLineEdit::keyPressEvent(QKeyEvent* pKey) +{ + switch(pKey->key()) { + case Qt::Key_Up: + case Qt::Key_Down: + case Qt::Key_PageUp: + case Qt::Key_PageDown: + emit keyPressed(pKey); + break; + + default: + QLineEdit::keyPressEvent(pKey); + break; + } +} + +/** + * Class constructor. + * @param pParent Owner list view widget + */ +ListToolTip::ListToolTip(SearchList* pParent) : + QToolTip(pParent->getList()->viewport()), + m_pList(pParent) +{ +} + +/** + * Displays a tool-tip according to the current location of the mouse + * pointer. + * @param pt The mouse pointer coordinates + */ +void ListToolTip::maybeTip(const QPoint& pt) +{ + QString str; + QListView* pList; + QListViewItem* pItem; + + // Get the item at the given point + pList = m_pList->getList(); + pItem = pList->itemAt(pt); + if (pItem == NULL) + return; + + // Get the tip string for this item + if (!m_pList->getTip(pItem, str)) + return; + + // Get the bounding rectangle of the item + const QRect rcItem = pList->itemRect(pItem); + if (!rcItem.isValid()) + return; + + // Get the header coordinates + const QRect rcHead = pList->header()->rect(); + if (!rcHead.isValid()) + return; + + // Calculate the tool-tip rectangle + QRect rcCell(rcHead.left(), rcItem.top(), rcItem.width(), rcItem.height()); + + // Display the tool-tip + tip(rcCell, str); +} + +/** + * Class constructor. + * @param nSearchCol The list column on which to perform string look-ups + * @param pParent The parent widget + * @param szName The widget's name + */ +SearchList::SearchList(int nSearchCol, QWidget* pParent, const char* szName) : + QVBox(pParent, szName), + m_nSearchCol(nSearchCol) +{ + // Create the child widgets + m_pEdit = new SearchLineEdit(this); + m_pList = new QListView(this); + + // Set up the tooltip generator + QToolTip::remove(m_pList); + m_pToolTip = new ListToolTip(this); + + connect(m_pEdit, SIGNAL(textChanged(const QString&)), this, + SLOT(slotFindItem(const QString&))); + connect(m_pList, SIGNAL(doubleClicked(QListViewItem*)), this, + SLOT(slotItemSelected(QListViewItem*))); + connect(m_pList, SIGNAL(returnPressed(QListViewItem*)), this, + SLOT(slotItemSelected(QListViewItem*))); + connect(m_pEdit, SIGNAL(returnPressed()), this, + SLOT(slotItemSelected())); + connect(m_pEdit, SIGNAL(keyPressed(QKeyEvent*)), this, + SLOT(slotKeyPressed(QKeyEvent*))); +} + +/** + * Class destructor. + */ +SearchList::~SearchList() +{ + delete m_pToolTip; +} + +/** + * Sets the keyboad focus to the search box. + */ +void SearchList::slotSetFocus() +{ + m_pEdit->setFocus(); +} + +/** + * Selects a list item whose string begins with the text entered in the edit + * widget. + * This slot is connected to the textChanged() signal of the line edit widget. + * @param sText The new text in the edit widget + */ +void SearchList::slotFindItem(const QString& sText) +{ + QListViewItem* pItem; + + // Try to find an item that contains this text + // Priority to exactly matched, + // then try to find line begins with the text, + // and if not found, then try to find the line contains the text + pItem = m_pList->findItem(sText, m_nSearchCol, + ExactMatch | BeginsWith | Contains); + + // Select this item + if (pItem != 0) { + m_pList->setSelected(pItem, true); + m_pList->ensureItemVisible(pItem); + } +} + +/** + * Lets inheriting classes process an item selection made through the list + * widget. + * This slot is connected to the doubleClicked() and returnPressed() + * signals of the list widget. + */ +void SearchList::slotItemSelected(QListViewItem* pItem) +{ + processItemSelected(pItem); + m_pEdit->setText(""); +} + +/** + * Lets inheriting classes process an item selection made through the edit + * widget. + * This slot is connected to the returnPressed() signal of the edit widget. + */ +void SearchList::slotItemSelected() +{ + QListViewItem* pItem; + + if ((pItem = m_pList->selectedItem()) != NULL) { + m_pEdit->setText(pItem->text(m_nSearchCol)); + processItemSelected(pItem); + } + + m_pEdit->setText(""); +} + +#define SEARCH_MATCH(pItem) \ + pItem->text(m_nSearchCol).startsWith(m_pEdit->text()) + +/** + * Sets a new current item based on key events in the edit box. + * This slot is connected to the keyPressed() signal of the edit widget. + * @param pKey The key evant passed by the edit box + */ +void SearchList::slotKeyPressed(QKeyEvent* pKey) +{ + QListViewItem* pItem, * pNewItem; + int nPageSize, nPos; + + // Select the current item, or the first one if there is no current item + pItem = m_pList->currentItem(); + + // Set a new current item based on the pressed key + switch (pKey->key()) { + case Qt::Key_Up: + if (pItem) { + for (pNewItem = pItem->itemAbove(); + pNewItem && !SEARCH_MATCH(pNewItem); + pNewItem = pNewItem->itemAbove()); + + if (pNewItem) + pItem = pNewItem; + } + break; + + case Qt::Key_Down: + if (pItem) { + for (pNewItem = pItem->itemBelow(); + pNewItem && !SEARCH_MATCH(pNewItem); + pNewItem = pNewItem->itemBelow()); + + if (pNewItem) + pItem = pNewItem; + } + break; + + case Qt::Key_PageUp: + nPageSize = m_pList->visibleHeight() / pItem->height(); + for (nPos = 0; + pItem && pItem->itemAbove() && (nPos < nPageSize); + nPos++) + pItem = pItem->itemAbove(); + break; + + case Qt::Key_PageDown: + nPageSize = m_pList->visibleHeight() / pItem->height(); + for (nPos = 0; + pItem && pItem->itemBelow() && (nPos < nPageSize); + nPos++) + pItem = pItem->itemBelow(); + break; + + default: + pKey->ignore(); + return; + } + + // Select the first item if no other item was selected + if (pItem == NULL) + pItem = m_pList->firstChild(); + + // Select the new item + if (pItem) { + m_pList->setSelected(pItem, true); + m_pList->ensureItemVisible(pItem); + } +} + +#include "searchlist.moc" diff --git a/src/searchlist.h b/src/searchlist.h new file mode 100644 index 0000000..ffa7ac8 --- /dev/null +++ b/src/searchlist.h @@ -0,0 +1,144 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef SEARCHLIST_H +#define SEARCHLIST_H + +#include <qwidget.h> +#include <qvbox.h> +#include <qlineedit.h> +#include <qlistview.h> +#include <qtooltip.h> + +class SearchList; + +/** + * Defines a line text edit for searchable list view. + * The widget is based on QLineEdit with additional key functions + * Supported key events (up and down) are emitted as signals + * @author Albert Yosher + */ +class SearchLineEdit : public QLineEdit +{ + Q_OBJECT +public: + SearchLineEdit(QWidget* pParent) : QLineEdit(pParent) {}; + ~SearchLineEdit() {}; + +signals: + /** + * Emitted when one of the up/down or page up/page down keys were pressed + * inside this edit widget. + * @param pEvent The event received for this key press + */ + void keyPressed(QKeyEvent* pEvent); + +private: + virtual void keyPressEvent(QKeyEvent*); +}; + +/** + * A tool-tip class for the search list. + * Enables sub-classes of the list to provide a customised tool-tip for each + * list item. + * @author Gabor Fekete + */ +class ListToolTip : public QToolTip +{ +public: + ListToolTip(SearchList* pParent); + +protected: + virtual void maybeTip(const QPoint& pt); + +private: + /** The owner widget. */ + SearchList* m_pList; +}; + + +/** + * Defines a searchable list view. + * The widget is composed of a list view, and an edit box used to enter + * search data. Whenever the text in the edit box changes, the list view is + * set to point to the first item that matches the new text. + * @author Elad Lahav + */ +class SearchList : public QVBox +{ + Q_OBJECT + +public: + SearchList(int nSearchCol, QWidget* pParent = 0, const char* szName = 0); + ~SearchList(); + + /** + * @return A pointer to the list part of the widget. + */ + QListView* getList() { return m_pList; } + + /** + * Constructs a tool-tip for the given item. + * @param pItem The item for which a tip is required + * @param sTip The constructed tip string (on return) + * @return True to display the tip, false otherwise + */ + virtual bool getTip(QListViewItem* pItem, QString& sTip) = 0; + +public slots: + void slotSetFocus(); + +protected: + /** The search edit-box. */ + QLineEdit* m_pEdit; + + /** The list part of the widget. */ + QListView* m_pList; + + /** + * Called whenever the user selects an item in the list by either double- + * clicking it, or by highlighting the item and pressing the ENTER key. + * @param pItem The selected list item + */ + virtual void processItemSelected(QListViewItem* pItem) = 0; + +protected slots: + void slotFindItem(const QString&); + void slotItemSelected(QListViewItem*); + void slotItemSelected(); + void slotKeyPressed(QKeyEvent*); + +private: + /** Specifies the search column, i.e., the list column whose strings are + compared with the text in the search edit-box. */ + int m_nSearchCol; + + /** A tool-tip for the list entries. */ + ListToolTip* m_pToolTip; +}; + +#endif diff --git a/src/searchresultsdlg.cpp b/src/searchresultsdlg.cpp new file mode 100644 index 0000000..bb63fa5 --- /dev/null +++ b/src/searchresultsdlg.cpp @@ -0,0 +1,160 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qpushbutton.h> +#include <qlineedit.h> +#include <qcombobox.h> +#include <qradiobutton.h> +#include "searchresultsdlg.h" + +int SearchResultsDlg::s_nType = PlainText; +bool SearchResultsDlg::s_bCaseSensitive = true; +bool SearchResultsDlg::s_bNegate = false; + +/** + * Class constructor. + * @param pParent The parent widget + * @param szName Optional widget name + */ +SearchResultsDlg::SearchResultsDlg(QWidget* pParent, const char* szName) : + SearchResultsLayout(pParent, szName, true, 0) +{ + // Select the last selected type radio button + switch (s_nType) { + case PlainText: + m_pTextRadio->setChecked(true); + break; + + case RegExp: + m_pRegExpRadio->setChecked(true); + break; + + case SimpRegExp: + m_pSimpRegExpRadio->setChecked(true); + break; + } + + // Set the default value of the check-boxes + m_pCaseSenCheck->setChecked(s_bCaseSensitive); + m_pNegateCheck->setChecked(s_bNegate); + + // Terminate the dialogue when either the "OK" or "Cancel" buttons are + // clicked + connect(m_pOKButton, SIGNAL(clicked()), this, SLOT(accept())); + connect(m_pCancelButton, SIGNAL(clicked()), this, SLOT(reject())); +} + +/** + * Class destructor. + */ +SearchResultsDlg::~SearchResultsDlg() +{ +} + +/** + * Determines the default column on which to search. + * The column's name appears in the column combo-box. + */ +void SearchResultsDlg::setColumn(int nCol) +{ + m_pColumnCB->setCurrentItem(nCol); +} + +/** + * @return The selected column on which to perform the search + */ +int SearchResultsDlg::getColumn() +{ + return m_pColumnCB->currentItem(); +} + +/** + * Creates a regular expression based on the given pattern and type of search. + * @param re A regular expression object to set + */ +void SearchResultsDlg::getPattern(QRegExp& re) +{ + QString sPattern; + + sPattern = m_pSearchEdit->text(); + + // Create the regular expression + switch (s_nType) { + case PlainText: + re.setPattern(QRegExp::escape(sPattern)); + re.setWildcard(false); + break; + + case RegExp: + re.setPattern(sPattern); + re.setWildcard(false); + break; + + case SimpRegExp: + re.setPattern(sPattern); + re.setWildcard(true); + break; + } + + // Set the case-(in)sensitive parameter + re.setCaseSensitive(s_bCaseSensitive); +} + +/** + * Reads user values from the widgets, and closes the dialogue. + * This slot is connected to the clicked() signal emitted by the "OK" button. + */ +void SearchResultsDlg::accept() +{ + QString sText; + + // Determine the selected type and store its value for the next invocation + if (m_pTextRadio->isChecked()) + s_nType = PlainText; + else if (m_pRegExpRadio->isChecked()) + s_nType = RegExp; + else if (m_pSimpRegExpRadio->isChecked()) + s_nType = SimpRegExp; + + // Determine search parameters + s_bCaseSensitive = m_pCaseSenCheck->isChecked(); + s_bNegate = m_pNegateCheck->isChecked(); + + // Remove white space from the search text + sText = m_pSearchEdit->text(); + sText.stripWhiteSpace(); + if (sText.isEmpty()) { + QDialog::reject(); + return; + } + + // Close the dialogue + QDialog::accept(); +} + +#include "searchresultsdlg.moc" + diff --git a/src/searchresultsdlg.h b/src/searchresultsdlg.h new file mode 100644 index 0000000..d636f8f --- /dev/null +++ b/src/searchresultsdlg.h @@ -0,0 +1,74 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef SEARCHRESULTSDLG_H +#define SEARCHRESULTSDLG_H + +#include <qregexp.h> +#include <qcheckbox.h> +#include "searchresultslayout.h" + +/** + * A dialogue for defining searches on query results. + * The dialogue is activated from the query results menu. + * @author Elad Lahav + */ +class SearchResultsDlg : public SearchResultsLayout +{ + Q_OBJECT + +public: + SearchResultsDlg(QWidget* pParent = 0, const char* szName = 0); + ~SearchResultsDlg(); + + void setColumn(int); + int getColumn(); + void getPattern(QRegExp&); + + /** + * @return true if the search pattern should be negated, false otherwise + */ + bool isNegated() { return m_pNegateCheck->isChecked(); } + +protected slots: + virtual void accept(); + +private: + /** Possible search types. */ + enum { PlainText = 0, RegExp, SimpRegExp }; + + /** Remembers the last search type. */ + static int s_nType; + + /** Remembers the last value of the Case Sensitive check-box. */ + static bool s_bCaseSensitive; + + /** Remembers the last value of the Negate Search check-box. */ + static bool s_bNegate; +}; + +#endif diff --git a/src/searchresultslayout.ui b/src/searchresultslayout.ui new file mode 100644 index 0000000..cdcde53 --- /dev/null +++ b/src/searchresultslayout.ui @@ -0,0 +1,214 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>SearchResultsLayout</class> +<widget class="QDialog"> + <property name="name"> + <cstring>SearchResultsLayout</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>361</width> + <height>307</height> + </rect> + </property> + <property name="caption"> + <string>Filter Query Results</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout2</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Search For:</string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>m_pSearchEdit</cstring> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout4</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Search In:</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>171</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QComboBox"> + <item> + <property name="text"> + <string>Function</string> + </property> + </item> + <item> + <property name="text"> + <string>File</string> + </property> + </item> + <item> + <property name="text"> + <string>Line</string> + </property> + </item> + <item> + <property name="text"> + <string>Text</string> + </property> + </item> + <property name="name"> + <cstring>m_pColumnCB</cstring> + </property> + </widget> + </hbox> + </widget> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>buttonGroup1</cstring> + </property> + <property name="title"> + <string>Search Type</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>m_pTextRadio</cstring> + </property> + <property name="text"> + <string>Plain Text</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>m_pRegExpRadio</cstring> + </property> + <property name="text"> + <string>RegE&xp</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>m_pSimpRegExpRadio</cstring> + </property> + <property name="text"> + <string>Simplified RegExp</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_pCaseSenCheck</cstring> + </property> + <property name="text"> + <string>Case Sensitive</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_pNegateCheck</cstring> + </property> + <property name="text"> + <string>Negate Search</string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout3</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>201</width> + <height>21</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pOKButton</cstring> + </property> + <property name="text"> + <string>OK</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pCancelButton</cstring> + </property> + <property name="text"> + <string>Cancel</string> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<tabstops> + <tabstop>m_pSearchEdit</tabstop> + <tabstop>m_pColumnCB</tabstop> + <tabstop>m_pTextRadio</tabstop> + <tabstop>m_pRegExpRadio</tabstop> + <tabstop>m_pSimpRegExpRadio</tabstop> + <tabstop>m_pCaseSenCheck</tabstop> + <tabstop>m_pOKButton</tabstop> + <tabstop>m_pCancelButton</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/src/symbolcompletion.cpp b/src/symbolcompletion.cpp new file mode 100644 index 0000000..2ec8194 --- /dev/null +++ b/src/symbolcompletion.cpp @@ -0,0 +1,344 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qtimer.h> +#include <klocale.h> +#include "symbolcompletion.h" + +bool SymbolCompletion::s_bACEnabled; +uint SymbolCompletion::s_nACMinChars; +uint SymbolCompletion::s_nACDelay; +uint SymbolCompletion::s_nACMaxEntries; + +/** + * Class constructor. + * @param pEditor The editor object for which symbol completion is required + * @param pParent Parent object + * @param szName Optional object name + */ +SymbolCompletion::SymbolCompletion(SymbolCompletion::Interface* pEditor, + QObject* pParent, const char* szName) : + QObject(pParent, szName), + m_pEditor(pEditor), + m_pCCObject(NULL) +{ + // Initialise member objects + m_pCscope = new CscopeFrontend(); + m_pAutoCompTimer = new QTimer(this); + + // Add entries to the completion list when they are available + connect(m_pCscope, SIGNAL(dataReady(FrontendToken*)), this, + SLOT(slotAddEntry(FrontendToken*))); + + // Show the completion list when the query finishes + connect(m_pCscope, SIGNAL(finished(uint)), this, + SLOT(slotQueryFinished(uint))); + + // Initiate automatic symbol completion when timer expires + connect(m_pAutoCompTimer, SIGNAL(timeout()), this, + SLOT(slotAutoCompleteTimeout())); +} + +/** + * Class destructor. + */ +SymbolCompletion::~SymbolCompletion() +{ + delete m_pCscope; +} + +/** + * Stops a completion process. + * This includes killing a running query, and stoping the auto-completion + * timer. + */ +void SymbolCompletion::abort() +{ + if (m_pCscope->isRunning()) + m_pCscope->kill(); + + m_pAutoCompTimer->stop(); +} + +/** + * Configures auto-completion parameters. + * @param bEnabled true to enable auto-completion, false otherwise + * @param nMinChars Minimal number of characters a symbol needs to start + * auto-completion + * @param nDelay Auto-completion time interval (in milliseconds) + * @param nMaxEntries The maximal number of completion entries + */ +void SymbolCompletion::initAutoCompletion(bool bEnabled, uint nMinChars, + uint nDelay, uint nMaxEntries) +{ + s_bACEnabled = bEnabled; + s_nACMinChars = nMinChars; + s_nACDelay = nDelay; + s_nACMaxEntries = nMaxEntries; +} + +/** + * Starts a completion process immediately for the symbol currently under the + * cursor in the editor object. + * Symbol completion is only available if the cursor is positioned at the end + * of the symbol. + */ +void SymbolCompletion::slotComplete() +{ + QString sSymbol; + uint nPosInWord; + + // Read the symbol currently under the cursor + sSymbol = m_pEditor->getWordUnderCursor(&nPosInWord); + + // The completion was triggered by user + m_bAutoCompletion = false; + + // start completion process, prefix is only on the left from the cursor + complete(sSymbol.left(nPosInWord)); +} + +/** + * Initiates an auto-completion timer. + * When the timer times-out, is starts the symbol completion process. + */ +void SymbolCompletion::slotAutoComplete() +{ + if (s_bACEnabled) + m_pAutoCompTimer->start(s_nACDelay, true); +} + +/** + * Creates a list of possible completions to the symbol currently being + * edited. + * @param sPrefix The symbol to complete + * @param nMaxEntries The maximal number of entries to display + */ +void SymbolCompletion::complete(const QString& sPrefix, int nMaxEntries) +{ + // Create a regular expression to extract symbol names from the query + // results + m_reSymbol.setPattern(sPrefix + "[a-zA-Z0-9_]*"); + + // If the new prefix is itself a prefix of the old one, we only need to + // filter the current entries + if (!m_sPrefix.isEmpty() && sPrefix.startsWith(m_sPrefix)) { + filterEntries(); + m_sPrefix = sPrefix; + slotQueryFinished(0); + return; + } + + // Prepare member variables + m_sPrefix = sPrefix; + m_nMaxEntries = nMaxEntries; + m_elEntries.clear(); + + // Run the code-completion query + m_pCscope->query(CscopeFrontend::Definition, sPrefix + ".*"); +} + +/** + * Removes from the current completion list all symbols that do not match + * the current regular expression. + * This function is used to aviod requerying the database on certain + * situations. + */ +void SymbolCompletion::filterEntries() +{ + EntryList::Iterator itr; + + // Iterate over the list and check each entry against the current RE + for (itr = m_elEntries.begin(); itr != m_elEntries.end();) { + if (m_reSymbol.search((*itr).text) == -1) + itr = m_elEntries.erase(itr); + else + ++itr; + } +} + +/** + * Conevrts the completion list into a single-entry one, containing the given + * message. + * @param sMsg The text of the message to include in the list. + */ +void SymbolCompletion::makeErrMsg(const QString& sMsg) +{ + Entry entry; + + // Clear the current list + m_elEntries.clear(); + + // Create the message item and add it to the list + entry.text = sMsg; + entry.userdata = "NO_INSERT"; // The message should not be insertable + m_elEntries.append(entry); + + // Make sure a new completion request will start a new query + m_sPrefix = ""; +} + +/** + * Creates a new entry in the list when a query record is available. + * This slot is connected to the dataReady() signal of the CscopeFrontend + * object. + * @param pToken Points to the head of a record's linked-list + */ +void SymbolCompletion::slotAddEntry(FrontendToken* pToken) +{ + Entry entry; + QString sText; + + // Do not add entries beyond the requested limit + if (m_elEntries.count() > m_nMaxEntries) + return; + + // Get the line text + pToken = pToken->getNext()->getNext()->getNext(); + sText = pToken->getData(); + + // Find the symbol within the line + if (m_reSymbol.search(sText) == -1) + return; + + // Add the new entry to the completion list + entry.text = m_reSymbol.capturedTexts().first(); + entry.userdata = ""; + entry.comment = sText; + + m_elEntries.append(entry); +} + +/** + * Displays a code completion list, based on the results of the last query. + * @param nRecords (ingnored) + */ +void SymbolCompletion::slotQueryFinished(uint /* nRecords */) +{ + KTextEditor::CodeCompletionInterface* pCCI; + uint nEntryCount; + EntryList::Iterator itr; + QString sPrevText; + + // Get the number of entries + nEntryCount = m_elEntries.count(); + + // Do not show the box the only completion option is the prefix itself + if (m_bAutoCompletion && (nEntryCount == 1) && + (m_elEntries.first().text == m_sPrefix)) { + return; + } + + // Get a pointer to the CC interface + m_pCCObject = m_pEditor->getCCObject(); + pCCI = dynamic_cast<KTextEditor::CodeCompletionInterface*>(m_pCCObject); + if (!pCCI) + return; + + // Insert the correct part of the completed symbol, when chosen by the + // user + connect(m_pCCObject, + SIGNAL(filterInsertString(KTextEditor::CompletionEntry*, QString*)), + this, + SLOT(slotFilterInsert(KTextEditor::CompletionEntry*, QString*))); + + // Check the number of entries in the list + if (nEntryCount == 0) { + // No completion options, display an appropriate message + makeErrMsg(i18n("No matching completion found...")); + } + else if (nEntryCount > m_nMaxEntries) { + // The query has resulted in too many entries, display an + // appropriate message + makeErrMsg(i18n("Too many options...")); + } + else { + // Sort the entries + m_elEntries.sort(); + + // Make sure entries are unique + for (itr = m_elEntries.begin(); itr != m_elEntries.end();) { + if ((*itr).text == sPrevText) { + itr = m_elEntries.erase(itr); + } + else { + sPrevText = (*itr).text; + ++itr; + } + } + } + + // Display the completion list + pCCI->showCompletionBox(m_elEntries); +} + +/** + * Determines which part of the completion entry should be added to the code + * when that entry is selected. + * @param pEntry Points to the selected entry + * @param pTextToInsert Contains the string to insert, upon return + */ +void SymbolCompletion::slotFilterInsert(KTextEditor::CompletionEntry* pEntry, + QString* pTextToInsert) +{ + // Insert the completed entry, unless it contains an error message + if (pEntry->userdata.isEmpty()) + *pTextToInsert = pEntry->text.mid(m_sPrefix.length()); + else + *pTextToInsert = ""; + + // Disconnect the CC object signals + disconnect(m_pCCObject, 0, this, 0); + m_pCCObject = NULL; +} + +/** + * Checks if the current symbol is eligible for auto-completion, and if so, + * starts the completion process. + * Auto-completion is performed for symbols that have the required minimal + * number of entries, and the cursor is positioned at the end of the word. + * This slot is connected to the timeout() signal of the auto-completion + * timer. + */ +void SymbolCompletion::slotAutoCompleteTimeout() +{ + QString sPrefix; + uint nPosInWord, nLength; + + // Read the symbol currently under the cursor + sPrefix = m_pEditor->getWordUnderCursor(&nPosInWord); + nLength = sPrefix.length(); + + // Check conditions, and start the completion process + if ((nLength >= s_nACMinChars) && (nPosInWord == nLength)) { + // The completion was triggered by auto-completion + m_bAutoCompletion = true; + complete(sPrefix, s_nACMaxEntries); + } +} + +#include "symbolcompletion.moc" diff --git a/src/symbolcompletion.h b/src/symbolcompletion.h new file mode 100644 index 0000000..6fe57e2 --- /dev/null +++ b/src/symbolcompletion.h @@ -0,0 +1,195 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef SYMBOLCOMPLETION_H +#define SYMBOLCOMPLETION_H + +#include <qobject.h> +#include <qregexp.h> +#include <ktexteditor/codecompletioninterface.h> +#include <ktexteditor/view.h> +#include "cscopefrontend.h" + +/** + * This class executes symbol definition queries based on symbol prefixes. + * The results can then be displayed as symbol completion lists. + * @author Albert Yosher + */ +class SymbolCompletion : public QObject +{ + Q_OBJECT + +public: + /** + * A pure-virtual class that allows a SymbolCompletion object access to + * text-editing objects. + * Classes that wish to utilise SymbolCompletion need to inplement this + * interface. + * @author Albert Yosher + */ + struct Interface + { + /** + * Class destructor. + * NOTE: A virtual destructor is required by GCC 4.0 + */ + virtual ~Interface() {} + + /** + * Returns the word currently under the editing cursor. + * Symbol completion will be provided for this word only if the cursor + * is positioned at the end of this word. + * @param pPosInWord Set this value to the offset in the word on + * which the cursor is positioned + */ + virtual QString getWordUnderCursor(uint* pPosInWord) = 0; + + /** + * Returns a target object for displaying the completion list. + * @return A pointer to an object implementing + * KTextEditor::CodeCompletionInterface, or NULL if the + * implementation does not support this interface. + */ + virtual QObject* getCCObject() = 0; + }; + + SymbolCompletion(SymbolCompletion::Interface*, QObject* pParent = 0, + const char* szName = 0); + ~SymbolCompletion(); + + void abort(); + + static void initAutoCompletion(bool, uint, uint, uint); + +public slots: + void slotComplete(); + void slotAutoComplete(); + +private: + /** + * Symbol completion entry object, used in the completion list. + * Implements operators required for sorting the completion list. + * @author Albert Yosher + */ + class Entry : public KTextEditor::CompletionEntry + { + public: + /** + * Determines whether a given entry is smaller than this one. + * @param entry The entry to compare with + * @return true if the given entry is smaller, false otherwise + */ + bool operator < (const SymbolCompletion::Entry& entry) const { + return (text < entry.text); + } + + /** + * Determines whether a given entry is equal to this one. + * @param entry The entry to compare with + * @return true if the given entry equals this one, false otherwise + */ + bool operator == (const SymbolCompletion::Entry& entry) const { + return (text == entry.text); + } + }; + + /** + * A sortable version of the value list used by CodeCompletionInterface. + * @author Albert Yosher + */ + class EntryList : public QValueList<Entry> + { + public: + /** + * Sorts completion list. + */ + void sort() { qHeapSort(*this); } + + /** + * Type casting support required for calling showCompletionBox(). + * @return A casted reference to this object + */ + operator QValueList<KTextEditor::CompletionEntry>() + { return *(QValueList<KTextEditor::CompletionEntry>*)this; } + }; + + /** Editor object for which symbol completion is provided. */ + Interface* m_pEditor; + + /** An object that supports KTextEditor::CodeCompletionInterface, as + supplied by the editor. */ + QObject* m_pCCObject; + + /** Cscope process used to run completion queries. */ + CscopeFrontend* m_pCscope; + + /** The prefix used for the current query. */ + QString m_sPrefix; + + /** A list of possible completions for the given prefix. */ + EntryList m_elEntries; + + /** The maximal number of completions to accept. */ + uint m_nMaxEntries; + + /** Regular expression for extracting a symbol out of Cscope's text field. + NOTE: This member is required due to a bug in Cscope that renders the + symbol field useless. */ + QRegExp m_reSymbol; + + /** Auto-completion timer. */ + QTimer* m_pAutoCompTimer; + + /** Auto-completion flag */ + bool m_bAutoCompletion; + + void complete(const QString&, int nMaxEntries = 1000); + void filterEntries(); + void makeErrMsg(const QString&); + + /** true if auto-completion is enabled, false otherwise. */ + static bool s_bACEnabled; + + /** The minimum number of characters a symbol must have for + auto-completion. */ + static uint s_nACMinChars; + + /** The interval between the time slotAutoComplete() is called and the + time the completion process begins (in milliseconds). */ + static uint s_nACDelay; + + /** The maximal number of entries for auto-completion. */ + static uint s_nACMaxEntries; + +private slots: + void slotAutoCompleteTimeout(); + void slotAddEntry(FrontendToken*); + void slotQueryFinished(uint); + void slotFilterInsert(KTextEditor::CompletionEntry*, QString*); +}; + +#endif diff --git a/src/symboldlg.cpp b/src/symboldlg.cpp new file mode 100644 index 0000000..3301678 --- /dev/null +++ b/src/symboldlg.cpp @@ -0,0 +1,334 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qlabel.h> +#include <qlistview.h> +#include <qpushbutton.h> +#include <qradiobutton.h> +#include <qcheckbox.h> +#include <qgroupbox.h> +#include <kcombobox.h> +#include <klocale.h> +#include "symboldlg.h" +#include "cscopefrontend.h" +#include "kscopeconfig.h" + +QStringList SymbolDlg::s_slHistory; + +/** + * Class constructor. + * @param pParent Parent widget + * @param szName This widget's name + */ +SymbolDlg::SymbolDlg(QWidget* pParent, const char* szName) : + SymbolLayout(pParent, szName, true, 0), + m_progress(m_pHintList) +{ + // Create a persistent Cscope process + m_pCscope = new CscopeFrontend(); + + // Initialise the hint list (hidden by default) + m_pHintList->addColumn(i18n("Suggested Symbols")); + m_pHintList->hide(); + ((QWidget*)m_pHintGroup)->hide(); + m_pBeginWithRadio->toggle(); + adjustSize(); + + // Close the dialogue when either the "OK" or "Cancel" button are clicked + connect(m_pOKButton, SIGNAL(clicked()), this, SLOT(accept())); + connect(m_pCancelButton, SIGNAL(clicked()), this, SLOT(reject())); + + // Run a symbol completion query when the "Hint" button is clicked + connect(m_pHintButton, SIGNAL(clicked()), this, SLOT(slotHintClicked())); + + // Add results to the hint list + connect(m_pCscope, SIGNAL(dataReady(FrontendToken*)), this, + SLOT(slotHintDataReady(FrontendToken*))); + + // Set hint button availability based on the type of query + connect(m_pTypeCombo, SIGNAL(activated(int)), this, + SLOT(slotTypeChanged(int))); + + // Selecting an item in the hint list sets it as the current text + connect(m_pHintList, SIGNAL(selectionChanged(QListViewItem*)), this, + SLOT(slotHintItemSelected(QListViewItem*))); + + // Double-clicking an item in the hint list accepts that item as the + // result of the query (i.e., the item is selcted and the dialogue is + // closed) + connect(m_pHintList, SIGNAL(doubleClicked(QListViewItem*)), this, + SLOT(accept())); + + // Refresh the hint list when the hint options change + connect(m_pBeginWithRadio, SIGNAL(toggled(bool)), this, + SLOT(slotHintOptionChanged(bool))); + connect(m_pContainRadio, SIGNAL(toggled(bool)), this, + SLOT(slotHintOptionChanged(bool))); + + // Show hint query progress information + connect(m_pCscope, SIGNAL(progress(int, int)), this, + SLOT(slotHintProgress(int, int))); + connect(m_pCscope, SIGNAL(finished(uint)), this, + SLOT(slotHintFinished(uint))); +} + +/** + * Class destructor. + */ +SymbolDlg::~SymbolDlg() +{ + delete m_pCscope; +} + +/** + * Displays the requested type of query in the type combo-box. + * @param nType The requested type + */ +void SymbolDlg::setType(uint nType) +{ + m_pTypeCombo->setCurrentItem(nType); + slotTypeChanged(nType); +} + +/** + * @param sSymbol The initial text of the combo-box + */ +void SymbolDlg::setSymbol(const QString& sSymbol) +{ + m_pSymbolHC->setCurrentText(sSymbol); +} + +/** + * @param slSymHistory A list of previously queried symbols + */ +void SymbolDlg::setHistory(QStringList& slSymHistory) +{ + m_pSymbolHC->setHistoryItems(slSymHistory); +} + +/** + * @return The current text of the symbol combo-box + */ +QString SymbolDlg::getSymbol() const +{ + QString sResult; + + sResult = m_pSymbolHC->currentText().stripWhiteSpace(); + if (m_pSubStringCheck->isChecked()) + sResult = ".*" + sResult + ".*"; + + return sResult; +} + +/** + * @return The type of query requested by the user + * @note The returned value does not conform to the type used for running + * Cscope queries. Use getQueryType() to translate between these + * values. + */ +uint SymbolDlg::getType() const +{ + return m_pTypeCombo->currentItem(); +} + +bool SymbolDlg::getCase() const +{ + return !m_pCaseCheck->isChecked(); +} + +/** + * A convinience static function for creating and showing SymbolDlg dialogue. + * @param pParent The parent widget + * @param nType The type of query requested by the user (may be + * changed in the dialogue) + * @param sSymbol The initial text of the combo-box + * @return The text entered by the user in the symbol combo-box, or an empty + * string if the dialogue was cancelled + */ +QString SymbolDlg::promptSymbol(QWidget* pParent, uint& nType, + const QString& sSymbol, bool& bCase) +{ + SymbolDlg dlg(pParent); + + // Initialise the dialogue + dlg.setType(nType); + dlg.setHistory(s_slHistory); + dlg.setSymbol(sSymbol); + + // Display the dialogue + if (dlg.exec() != QDialog::Accepted) + return ""; + + // Return the text entered by the user + nType = dlg.getType(); + bCase = dlg.getCase(); + dlg.m_pSymbolHC->addToHistory(dlg.getSymbol()); + s_slHistory = dlg.m_pSymbolHC->historyItems(); + return dlg.getSymbol(); +} + +/** + * Translates a symbol dialogue type into a Cscope query type. + * @param nType The type to translate + * @return A query type matching the symbol dialogue type + */ +uint SymbolDlg::getQueryType(uint nType) +{ + if (nType == CallTree) + return CscopeFrontend::None; + + if (nType <= Text) + return nType; + + return nType + 1; +} + +/** + * Runs a symbol definition query, looking for symbols starting with the + * currently entered text. + * If the hint list is not visible, it is shown first. + * This slot is connected to the clicked() signal of the "Hint" button. + */ +void SymbolDlg::slotHintClicked() +{ + QString sText, sRegExp; + + // Show the hint list if necessary + if (!m_pHintList->isVisible()) { + m_pHintList->show(); + ((QWidget*)m_pHintGroup)->show(); + adjustSize(); + } + + // Clear the previous contents + m_pHintList->clear(); + + // Get the currently entered text (must have at least one character) + sText = m_pSymbolHC->currentText().stripWhiteSpace(); + if (sText.isEmpty()) + return; + + // Create the regular expression + if (m_pBeginWithRadio->isOn()) + sRegExp = sText + "[a-zA-Z0-9_]*"; + else + sRegExp = "[a-zA-Z0-9_]*" + sText + "[a-zA-Z0-9_]*"; + + m_reHint.setPattern(sRegExp); + + // Run a Cscope symbol definition query using a regular expression + m_pCscope->query(CscopeFrontend::Definition, sRegExp); +} + +/** + * Called when a new record is ready to be added to the hint list. + * NOTE: Cscope 15.5 has a bug where the "function" field of the record + * displays the regular expression instead of the matched symbol name. For + * this reason, we need to extract the symbol from the "Text" field. + * @param pToken The head of the record's token list + */ +void SymbolDlg::slotHintDataReady(FrontendToken* pToken) +{ + QString sText; + + // Get the line text + pToken = pToken->getNext()->getNext()->getNext(); + sText = pToken->getData(); + + // Find the symbol within the line + if (m_reHint.search(sText) == -1) + return; + + // Find the symbol within the list, if found - do not add + if (m_pHintList->findItem(m_reHint.capturedTexts().first(), 0)) + return; + + // Add a list item + (void)new QListViewItem(m_pHintList, m_reHint.capturedTexts().first()); + +} + +/** + * Sets the text of a selected hint list item as the current text of the + * symbol combo-box. + * This slot is connected to the doubleClicked() signal of the hint list-view. + * @param pItem The clicked list item + */ +void SymbolDlg::slotHintItemSelected(QListViewItem* pItem) +{ + m_pSymbolHC->setCurrentText(pItem->text(0)); +} + +/** + * Refreshes the hint list based on the newly selected option. + * This slot is connected to the toggled() signal of the hint options radio + * buttons. + * NOTE: The list is only refreshed if the system profile is set to Fast. + * @param bOn true if the button was toggled on + */ +void SymbolDlg::slotHintOptionChanged(bool bOn) +{ + if (bOn && Config().getSysProfile() == KScopeConfig::Fast) + slotHintClicked(); +} + +/** + * Display a progress bar while the hint query is working. + * This slot is connected to the progress() signal emitted by the Cscope + * frontend object. + * @param nProgress Progress value + * @param nTotal The final expected value + */ +void SymbolDlg::slotHintProgress(int nProgress, int nTotal) +{ + m_progress.setProgress(nProgress, nTotal); +} + +/** + * Destroys all progress information widget when the query process terminates. + * This slot is connected to the finished() signal emitted by the Cscope + * process. + */ +void SymbolDlg::slotHintFinished(uint /* ignored */) +{ + m_progress.finished(); +} + +/** + * Enables/disables the hint button, based on the newly selected type. + * This slot is connected to the activated() signal of the type combo-box. + * @param nType The newly selected type + */ +void SymbolDlg::slotTypeChanged(int nType) +{ + if (nType == FileName || nType == Including) + m_pHintButton->setEnabled(false); + else + m_pHintButton->setEnabled(true); +} + +#include "symboldlg.moc" diff --git a/src/symboldlg.h b/src/symboldlg.h new file mode 100644 index 0000000..4360213 --- /dev/null +++ b/src/symboldlg.h @@ -0,0 +1,91 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef SYMBOLDLG_H +#define SYMBOLDLG_H + +#include <qregexp.h> +#include "symbollayout.h" +#include "cscopefrontend.h" + +/** + * A dialogue that prompts the user for the text of a query. + * When a query is requested, the user needs to fill in the required + * information (usually a symbol name). This dialogue allows the user to + * enter this information, as well as complete a symbol name, and use + * previously entered text. + * @author Elad Lahav + */ + +class SymbolDlg : public SymbolLayout +{ + Q_OBJECT + +public: + SymbolDlg(QWidget* pParent = 0, const char* szName = 0); + ~SymbolDlg(); + + enum { Reference = 0, Definition, Called, Calling, Text, Pattern, + FileName, Including, CallTree }; + + void setType(uint); + void setSymbol(const QString&); + void setHistory(QStringList&); + QString getSymbol() const; + uint getType() const; + bool getCase() const; + + static QString promptSymbol(QWidget*, uint&, const QString&, bool&); + static uint getQueryType(uint); + static void resetHistory() { s_slHistory.clear(); } + +private: + /** A cscope process used for symbol completion. */ + CscopeFrontend* m_pCscope; + + /** A regular expression for extracting the symbol name out of the text + token of a Cscope record. + @see note in slotHintDataReady(). */ + QRegExp m_reHint; + + /** Displays query progress information. */ + CscopeProgress m_progress; + + static QStringList s_slHistory; + +private slots: + void slotHintClicked(); + void slotHintDataReady(FrontendToken*); + void slotHintItemSelected(QListViewItem*); + void slotHintOptionChanged(bool); + void slotHintProgress(int, int); + void slotHintFinished(uint); + void slotTypeChanged(int); +}; + +#endif + diff --git a/src/symbollayout.ui b/src/symbollayout.ui new file mode 100644 index 0000000..09b2885 --- /dev/null +++ b/src/symbollayout.ui @@ -0,0 +1,297 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>SymbolLayout</class> +<widget class="QDialog"> + <property name="name"> + <cstring>SymbolLayout</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>343</width> + <height>456</height> + </rect> + </property> + <property name="caption"> + <string>KScope Query</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout15</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout14</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Type</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Symbol</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout13</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QComboBox"> + <item> + <property name="text"> + <string>References to</string> + </property> + </item> + <item> + <property name="text"> + <string>Definition of</string> + </property> + </item> + <item> + <property name="text"> + <string>Functions called by</string> + </property> + </item> + <item> + <property name="text"> + <string>Functions calling</string> + </property> + </item> + <item> + <property name="text"> + <string>Find text</string> + </property> + </item> + <item> + <property name="text"> + <string>Find EGrep pattern</string> + </property> + </item> + <item> + <property name="text"> + <string>Find file</string> + </property> + </item> + <item> + <property name="text"> + <string>Files #including</string> + </property> + </item> + <item> + <property name="text"> + <string>Call graph for</string> + </property> + </item> + <property name="name"> + <cstring>m_pTypeCombo</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="KHistoryCombo"> + <property name="name"> + <cstring>m_pSymbolHC</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="insertionPolicy"> + <enum>AtTop</enum> + </property> + <property name="duplicatesEnabled"> + <bool>false</bool> + </property> + </widget> + </vbox> + </widget> + </hbox> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_pSubStringCheck</cstring> + </property> + <property name="text"> + <string>Search for &a Sub-String</string> + </property> + <property name="accel"> + <string>Alt+A</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_pCaseCheck</cstring> + </property> + <property name="text"> + <string>Case Insensitive</string> + </property> + </widget> + <widget class="Line"> + <property name="name"> + <cstring>line2</cstring> + </property> + <property name="frameShape"> + <enum>HLine</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout3</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>71</width> + <height>21</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pOKButton</cstring> + </property> + <property name="text"> + <string>OK</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pHintButton</cstring> + </property> + <property name="text"> + <string>Hi&nt</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pCancelButton</cstring> + </property> + <property name="text"> + <string>Cancel</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QListView"> + <property name="name"> + <cstring>m_pHintList</cstring> + </property> + </widget> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>m_pHintGroup</cstring> + </property> + <property name="title"> + <string>Hint Options</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>m_pBeginWithRadio</cstring> + </property> + <property name="text"> + <string>S&ymbols Beginning With...</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>m_pContainRadio</cstring> + </property> + <property name="text"> + <string>Sym&bols Containing...</string> + </property> + </widget> + </vbox> + </widget> + </vbox> +</widget> +<tabstops> + <tabstop>m_pSymbolHC</tabstop> + <tabstop>m_pTypeCombo</tabstop> + <tabstop>m_pSubStringCheck</tabstop> + <tabstop>m_pOKButton</tabstop> + <tabstop>m_pHintButton</tabstop> + <tabstop>m_pCancelButton</tabstop> + <tabstop>m_pHintList</tabstop> + <tabstop>m_pBeginWithRadio</tabstop> + <tabstop>m_pContainRadio</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kcombobox.h</includehint> + <includehint>klineedit.h</includehint> +</includehints> +</UI> diff --git a/src/tab_list.png b/src/tab_list.png Binary files differnew file mode 100644 index 0000000..b0cb478 --- /dev/null +++ b/src/tab_list.png diff --git a/src/tabwidget.cpp b/src/tabwidget.cpp new file mode 100644 index 0000000..2795a84 --- /dev/null +++ b/src/tabwidget.cpp @@ -0,0 +1,84 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qtooltip.h> +#include <klocale.h> +#include "tabwidget.h" +#include "kscopepixmaps.h" + +/** + * Class constructor. + * @param pParent A pointer to the parent widget + * @param szName Optional widget name + */ +TabWidget::TabWidget(QWidget* pParent, const char* szName) : + KTabWidget(pParent, szName) +{ + // Create a popup menu + m_pMenu = new QPopupMenu(this); + + // Set the current tab based on the menu selection + connect(m_pMenu, SIGNAL(activated(int)), this, SLOT(setCurrentPage(int))); + + // Create a button at the top-right corner of the tab widget + m_pButton = new QToolButton(this); + m_pButton->setIconSet(Pixmaps().getPixmap(KScopePixmaps::TabList)); + QToolTip::add(m_pButton, i18n("Shows a list of all open tabs")); + m_pButton->adjustSize(); + setCornerWidget(m_pButton, TopRight); + + // Show the popup-menu when the button is clicked + connect(m_pButton, SIGNAL(clicked()), this, SLOT(slotShowTabList())); +} + +/** + * Class destructor. + */ +TabWidget::~TabWidget() +{ +} + +/** + * Creates and displays a popup-menu containing all tab labels. + * This slot is connected to the clicked() signal emitted by the list button. + */ +void TabWidget::slotShowTabList() +{ + int i; + + // Delete the previous menu + m_pMenu->clear(); + + // Create and populate the menu + for (i = 0; i < count(); i++) + m_pMenu->insertItem(label(i), i); + + // Show the menu + m_pMenu->popup(mapToGlobal(m_pButton->pos())); +} + +#include "tabwidget.moc" diff --git a/src/tabwidget.h b/src/tabwidget.h new file mode 100644 index 0000000..74672ae --- /dev/null +++ b/src/tabwidget.h @@ -0,0 +1,59 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef TABWIDGET_H +#define TABWIDGET_H + + +#include <qtoolbutton.h> +#include <qpopupmenu.h> +#include <ktabwidget.h> + +/** + * An extension to the standard KDE tab widget that allows the user to select + * a tab from a list displayed as a popup menu. + * @author Elad Lahav + */ +class TabWidget : public KTabWidget +{ +Q_OBJECT +public: + TabWidget(QWidget* pParent = 0, const char* szName = 0); + ~TabWidget(); + +private: + /** The list button. */ + QToolButton* m_pButton; + + /** A popup-menu containing all tab labels. */ + QPopupMenu* m_pMenu; + +private slots: + void slotShowTabList(); +}; + +#endif diff --git a/src/treewidget.cpp b/src/treewidget.cpp new file mode 100644 index 0000000..b303194 --- /dev/null +++ b/src/treewidget.cpp @@ -0,0 +1,260 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include "treewidget.h" +#include "queryviewdriver.h" + +/** + * Class constructor. + * @param pParent Parent widget + * @param szName The name of the widget + */ +TreeWidget::TreeWidget(QWidget* pParent, const char* szName) : + QueryView(pParent, szName), + m_nQueryType(CscopeFrontend::Called) +{ + setRootIsDecorated(true); + + // Create a driver object + m_pDriver = new QueryViewDriver(this, this); + + // Query a tree item when it is expanded for the first time + connect(this, SIGNAL(expanded(QListViewItem*)), this, + SLOT(slotQueryItem(QListViewItem*))); +} + +/** + * Class destructor. + */ +TreeWidget::~TreeWidget() +{ +} + +/** + * Determines the mode of the tree. + * @param mode The new mode (@see Mode) + */ +void TreeWidget::setMode(Mode mode) +{ + m_nQueryType = (mode == Called) ? CscopeFrontend::Called : + CscopeFrontend::Calling; +} + +/** + * Sets a new root item for the tree. + * @param sFunc The name of the function to serve as root + */ +void TreeWidget::setRoot(const QString& sFunc) +{ + QListViewItem* pRoot; + + // Remove the current root, if any + if ((pRoot = firstChild()) != NULL) + delete pRoot; + + // Create a new root item + pRoot = new QListViewItem(this, sFunc); + pRoot->setExpandable(true); +} + +/** + * Runs a query on the root item. + */ +void TreeWidget::queryRoot() +{ + QListViewItem* pRoot; + + if ((pRoot = firstChild()) != NULL) + slotQueryItem(pRoot); +} + +/** + * Stores the tree contents in the given file. + * @param pFile An open file to write to + */ +void TreeWidget::save(FILE* pFile) +{ + QTextStream str(pFile, IO_WriteOnly); + QListViewItem* pRoot; + Encoder enc; + + if (m_nQueryType == CscopeFrontend::Called) + str << "calltree {" << endl; + else + str << "callingtree {" << endl; + + // Write the tree to the file + pRoot = firstChild(); + str << pRoot->text(0) << endl; + str << '{' << endl; + saveItems(pRoot->firstChild(), str, enc); + str << '}' << endl; + str << '}' << endl; +} + +/** + * Recursively writes tree items to a file. + * Given an item, the method writes this item and all of its siblings. + * Child items are written recursively. + * @param pItem The first item to write + * @param str An initialised text stream to use for writing + * @param enc An encoder for free-text strings + */ +void TreeWidget::saveItems(QListViewItem* pItem, QTextStream& str, Encoder& enc) +{ + // Iterate over all items in this level + for (; pItem != NULL; pItem = pItem->nextSibling()) { + // Write function parameters + str << pItem->text(0) << " [ " + << "kscope_file=\"" << pItem->text(1) << "\", " + << "kscope_line=" << pItem->text(2) << ", " + << "kscope_text=\"" << enc.encode(pItem->text(3)) << "\"" + << "]" << endl; + + // Write child items + str << "{" << endl; + saveItems(pItem->firstChild(), str, enc); + str << "}" << endl; + } +} + +/** + * Creates a new tree item showing a query result record. + * @param sFunc The name of the function + * @param sFile The file path + * @param sLine The line number in the above file + * @param sText The line's text + * @param pParent The parent for the new item + */ +void TreeWidget::addRecord(const QString& sFunc, const QString& sFile, + const QString& sLine, const QString& sText, QListViewItem* pParent) +{ + QListViewItem* pItem; + + pItem = new QueryViewItem(pParent, m_pLastItem, 2); + pItem->setText(0, sFunc); + pItem->setText(1, sFile); + pItem->setText(2, sLine); + pItem->setText(3, sText); + + pItem->setExpandable(true); + m_pLastItem = pItem; +} + +/** + * Called when a query running on the tree terminates. + * If there were no results, the item becomes non-expandable. + * NOTE: On top of its current behaviour, this function is required in order to + * override the default behaviour, as specified by QueryView. + * @param nResults Number of results + * @param pParent The item for which the query was executed + */ +void TreeWidget::queryFinished(uint nResults, QListViewItem* pParent) +{ + if (nResults == 0) + pParent->setExpandable(false); + else + pParent->setOpen(true); +} + +/** + * Runs a query on the given item, unless it was queried before. + * This slot is connected to the expanded() signal of the list view. + * @param pItem The item to query + */ +void TreeWidget::slotQueryItem(QListViewItem* pItem) +{ + // Do nothing if the item was already queried + // An item has been queried if it has children or marked as non-expandable + if (pItem->firstChild() != NULL || !pItem->isExpandable()) + return; + + // Run the query + m_pDriver->query(m_nQueryType, pItem->text(0), true, pItem); +} + +/** + * Hides all descendant that do not meet the given search criteria. + * This slot is connected to the search() signal of the QueryResultsMenu + * object. + * The search is incremental: only visible items are checked, so that a new + * search goes over the results of the previous one. + * @param pParent The parent item whose child are searched + * @param re The pattern to search + * @param nCol The list column to search in + */ +void TreeWidget::slotSearch(QListViewItem* pParent, const QRegExp& re, + int nCol) +{ + QListViewItem* pItem; + + // Get the first child + if (pParent != NULL) + pItem = pParent->firstChild(); + else + pItem = firstChild(); + + // Iterate over all child items + while (pItem != NULL) { + // Filter visible items only + if (pItem->isVisible() && re.search(pItem->text(nCol)) == -1) + pItem->setVisible(false); + + // Search child items recursively + slotSearch(pItem, re, nCol); + + pItem = pItem->nextSibling(); + } +} + +/** + * Makes all descendants of the given item visible. + * This slot is connected to the showAll() signal of the QueryResultsMenu + * object. + */ +void TreeWidget::slotShowAll(QListViewItem* pParent) +{ + QListViewItem* pItem; + + // Get the first child + if (pParent != NULL) + pItem = pParent->firstChild(); + else + pItem = firstChild(); + + // Iterate over all child items + while (pItem != NULL) { + pItem->setVisible(true); + + // Show child items recursively + slotShowAll(pItem); + + pItem = pItem->nextSibling(); + } +} + +#include "treewidget.moc" diff --git a/src/treewidget.h b/src/treewidget.h new file mode 100644 index 0000000..57c2a98 --- /dev/null +++ b/src/treewidget.h @@ -0,0 +1,82 @@ +/*************************************************************************** + * + * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#ifndef TREEWIDGET_H +#define TREEWIDGET_H + +#include "queryview.h" +#include "encoder.h" + +class QueryViewDriver; + +/** + * A tree-like widget displaying a hierarchical list of functions. + * The widget has two modes: called functions and calling functions. Depending + * on this mode, child items represent functions called by or calling their + * parent item. + * @author Elad Lahav + */ +class TreeWidget : public QueryView +{ + Q_OBJECT + +public: + TreeWidget(QWidget* pParent = 0, const char* szName = 0); + ~TreeWidget(); + + /** + * The type of tree to display. + */ + enum Mode { Called, Calling }; + + void setMode(Mode); + void setRoot(const QString&); + void queryRoot(); + void save(FILE*); + + virtual void addRecord(const QString&, const QString&, const QString&, + const QString&, QListViewItem*); + virtual void queryFinished(uint, QListViewItem*); + +protected slots: + virtual void slotSearch(QListViewItem*, const QRegExp&, int); + virtual void slotShowAll(QListViewItem*); + +private: + /** The CscopeFrontend query type to use (based on the current mode). */ + uint m_nQueryType; + + /** Runs queries and outputs the results as tree items. */ + QueryViewDriver* m_pDriver; + + void saveItems(QListViewItem*, QTextStream&, Encoder&); + +private slots: + void slotQueryItem(QListViewItem*); +}; + +#endif diff --git a/src/welcomedlg.ui b/src/welcomedlg.ui new file mode 100644 index 0000000..44c385c --- /dev/null +++ b/src/welcomedlg.ui @@ -0,0 +1,126 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>WelcomeDlg</class> +<widget class="QDialog"> + <property name="name"> + <cstring>WelcomeDlg</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>519</width> + <height>386</height> + </rect> + </property> + <property name="caption"> + <string>Welcome</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KTextBrowser"> + <property name="name"> + <cstring>m_pBrowser</cstring> + </property> + <property name="paletteBackgroundColor"> + <color> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </property> + <property name="text"> + <string><h1>Welcome to <font color="#c00000">KScope</font>!</h1> + +If this is the first time you are running Kscope, please follow these steps (click on the links for detailed instructions): +<p> +1. <a href="help:/kscope/configuration.html#config-progs">Configure</a> paths to the required back-end executables<br> +2. <a href="help:/kscope/projects.html#project-create">Create</a> a new project<br> +3. <a href="help:/kscope/projects.html#project-files">Populate</a> the project with source files<br> +4. <a href="help:/kscope/queries.html">Browse</a> the project and <a href="help:/kscope/editing.html">edit</a> files<br> + +</p> + +<p> +For more information, please take a look at KScope's <a href="help:/kscope">manual</a>, or visit the KScope <a href="http://kscope.sourceforge.net">website</a>. +</p> + +<p> +Enjoy! +</p> + +<p> +<font size="-1">This message will only appear once. Use the "<b>Help->Show Welcome Message...</b>" menu command to show it again at any time.</font> +</p></string> + </property> + </widget> + <widget class="Line"> + <property name="name"> + <cstring>line1</cstring> + </property> + <property name="frameShape"> + <enum>HLine</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout1</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>381</width> + <height>21</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_pCloseButton</cstring> + </property> + <property name="text"> + <string>Close</string> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<connections> + <connection> + <sender>m_pCloseButton</sender> + <signal>clicked()</signal> + <receiver>WelcomeDlg</receiver> + <slot>accept()</slot> + </connection> +</connections> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>ktextbrowser.h</includehint> +</includehints> +</UI> |