Reposition clear and done buttons on dialog
[kdevelop:kdevelop.git] / kdevelop / grepdialog.cpp
1 /***************************************************************************
2                           grepdialog.cpp  -  grep frontend                              
3                              -------------------                                         
4     copyright            : (C) 1999 by Bernd Gehrmann
5     email                : bernd@physik.hu-berlin.de
6  ***************************************************************************/
7
8 /***************************************************************************
9  *                                                                         *
10  *   This program is free software; you can redistribute it and/or modify  *
11  *   it under the terms of the GNU General Public License as published by  *
12  *   the Free Software Foundation; either version 2 of the License, or     *
13  *   (at your option) any later version.                                   * 
14  *                                                                         *
15  ***************************************************************************/
16
17
18 #include "grepdialog.h"
19 #include <qlayout.h>
20 #include <qlabel.h>
21 #include <qpushbutton.h>
22 #include <qlineedit.h>
23 #include <qcombobox.h>
24 #include <qcheckbox.h>
25 #include <qlistbox.h>
26 #include <qregexp.h>
27 #include <qlabel.h>
28 #include <kquickhelp.h>
29 #include <kbuttonbox.h>
30 #include <kfiledialog.h>
31 #include <kprocess.h>
32 #include <kapp.h>
33 #include <htmltoken.h>
34 #include <klocale.h>
35
36 const char *template_desc[] = {
37     "normal",
38     "assignment",
39     "->MEMBER(",
40     "class::MEMBER(",
41     "OBJECT->member(",
42     0
43 };
44
45 const char *template_str[] = {
46     "%s",
47     "\\<%s\\>[\t ]*=[^=]",
48     "\\->[\\t ]*\\<%s\\>[\\t ]*(",
49     "[a-z0-9_$]\\+[\\t ]*::[\\t ]*\\<%s\\>[\\t ]*(",
50     "\\<%s\\>[\\t ]*\\->[\\t ]*[a-z0-9_$]\\+[\\t ]*(",
51     0
52 };
53
54
55 GrepDialog::GrepDialog(QString dirname, QWidget *parent, const char *name)
56     : QDialog(parent, name, false), childproc(0)
57 {
58     setCaption(i18n("Search in Files..."));
59     
60     QGridLayout *layout = new QGridLayout(this, 6, 3, 10, 4);
61     layout->setColStretch(0, 10);
62     layout->addColSpacing(1, 10);
63     layout->setColStretch(1, 0);
64     layout->setColStretch(2, 1);
65     layout->addRowSpacing(1, 10);
66     layout->setRowStretch(1, 0);
67     layout->setRowStretch(2, 10);
68     layout->addRowSpacing(4, 10);
69     layout->setRowStretch(4, 0);
70     
71     QGridLayout *input_layout = new QGridLayout(4, 2, 4);
72     layout->addLayout(input_layout, 0, 0);
73     input_layout->setColStretch(0, 0);
74     input_layout->setColStretch(1, 20);
75
76     QLabel *pattern_label = new QLabel(i18n("&Pattern:"), this);
77     pattern_label->setFixedSize(pattern_label->sizeHint());
78     input_layout->addWidget(pattern_label, 0, 0, AlignRight | AlignVCenter);
79
80     pattern_edit = new QLineEdit(this);
81     pattern_label->setBuddy(pattern_edit);
82     pattern_edit->setFocus();
83     pattern_edit->setMinimumSize(pattern_edit->sizeHint());
84     input_layout->addWidget(pattern_edit, 0, 1);
85     
86     QLabel *template_label = new QLabel(i18n("&Template:"), this);
87     template_label->setFixedSize(template_label->sizeHint());
88     input_layout->addWidget(template_label, 1, 0, AlignRight | AlignVCenter);
89
90     QBoxLayout *template_layout = new QHBoxLayout(4);
91     input_layout->addLayout(template_layout, 1, 1);
92     
93     template_edit = new QLineEdit(this);
94     template_label->setBuddy(template_edit);
95     template_edit->setText(template_str[0]);
96     template_edit->setMinimumSize(template_edit->sizeHint());
97     template_layout->addWidget(template_edit);
98
99     QComboBox *template_combo = new QComboBox(false, this);
100     template_combo->insertStrList(template_desc);
101     template_combo->adjustSize();
102     template_combo->setFixedSize(template_combo->size());
103     template_layout->addWidget(template_combo);
104
105     QLabel *files_label = new QLabel(i18n("&Files:"), this);
106     files_label->setFixedSize(files_label->sizeHint());
107     input_layout->addWidget(files_label, 2, 0, AlignRight | AlignVCenter);
108
109     files_combo = new QComboBox(true, this);
110     files_label->setBuddy(files_combo->focusProxy());
111     files_combo->setMinimumSize(files_combo->sizeHint());
112     files_combo->insertItem("*.h,*.hxx,*.cpp,*.cc,*.C,*.cxx,*.idl,*.c");
113     files_combo->insertItem("*.cpp,*.cc,*.C,*.cxx,*.c");
114     files_combo->insertItem("*.h,*.hxx,*.idl");
115     files_combo->insertItem("*");
116     input_layout->addWidget(files_combo, 2, 1);
117
118     QLabel *dir_label = new QLabel(i18n("&Directory:"), this);
119     dir_label->setFixedSize(dir_label->sizeHint());
120     input_layout->addWidget(dir_label, 3, 0, AlignRight | AlignVCenter);
121
122     QBoxLayout *dir_layout = new QHBoxLayout(4);
123     input_layout->addLayout(dir_layout, 3, 1);
124     
125     dir_edit = new QLineEdit(this);
126     dir_label->setBuddy(dir_edit);
127     dir_edit->setText(dirname);
128     dir_edit->setMinimumSize(dir_edit->sizeHint());
129     dir_layout->addWidget(dir_edit, 10);
130
131     QPushButton *dir_button = new QPushButton(this, "dirButton");
132     QPixmap pix;
133     pix.load(KApplication::kde_datadir() + "/kdevelop/toolbar/open.xpm");
134     dir_button->setPixmap(pix);
135     dir_button->setFixedHeight(dir_edit->sizeHint().height());
136     dir_button->setFixedWidth(30);
137     dir_layout->addWidget(dir_button);
138     
139     recursive_box = new QCheckBox(i18n("&Recursive"), this);
140     recursive_box->setMinimumWidth(recursive_box->sizeHint().width());
141     recursive_box->setChecked(true);
142     dir_layout->addSpacing(10);
143     dir_layout->addWidget(recursive_box);
144
145     KButtonBox *actionbox = new KButtonBox(this, KButtonBox::VERTICAL);
146     layout->addWidget(actionbox, 0, 2);
147     actionbox->addStretch();
148     search_button = actionbox->addButton(i18n("&Search"));
149     search_button->setDefault(true);
150     cancel_button = actionbox->addButton(i18n("Cancel"));
151     cancel_button->setEnabled(false);
152     QPushButton *clear_button = actionbox->addButton(i18n("Clear"));
153     QPushButton *done_button = actionbox->addButton(i18n("Done"));
154     actionbox->addStretch();
155     actionbox->layout();
156
157     resultbox = new QListBox(this);
158     QFontMetrics rb_fm(resultbox->fontMetrics());
159     resultbox->setMinimumSize(rb_fm.width("0")*55,
160                               rb_fm.lineSpacing()*15);
161     layout->addMultiCellWidget(resultbox, 2, 2, 0, 2);
162
163     QFrame *status_frame = new QFrame(this);
164     status_frame->setFrameStyle(QFrame::Panel | QFrame::Sunken);
165     QBoxLayout *status_layout = new QHBoxLayout(status_frame, 2);
166
167     status_label = new QLabel(i18n("Ready"), status_frame);
168     status_layout->addWidget(status_label, 10);
169
170     matches_label = new QLabel(status_frame);
171     QFontMetrics ml_fm(matches_label->fontMetrics());
172     matches_label->setFixedWidth(ml_fm.width(i18n("9999 matches")));
173     matches_label->setFixedHeight(ml_fm.lineSpacing());
174     status_layout->addWidget(matches_label, 0);
175
176     status_layout->activate();
177     status_frame->adjustSize();
178     status_frame->setMinimumSize(status_frame->size());
179     layout->addMultiCellWidget(status_frame, 3, 3, 0, 2);
180
181     layout->activate();
182
183     KQuickHelp::add(pattern_edit,
184                     i18n("Enter the regular expression you want to search for here.\n"
185                          "Possible meta characters are:\n"
186                          "<bold>.</bold> - Matches any character\n"
187                          "<bold>^</bold> - Matches the beginning of a line\n"
188                          "<bold>$</bold> - Matches the end of a line\n"
189                          "<bold>\\\\\\<</bold> - Matches the beginning of a word\n"
190                          "<bold>\\\\\\></bold> - Matches the end of a word\n"
191                          "\n"
192                          "The following repetition operators exist:\n"
193                          "<bold>?</bold> - The preceding item is matches at most once\n"
194                          "<bold>*</bold> - The preceding item is matched zero or more times\n"
195                          "<bold>+</bold> - The preceding item is matched once or more times\n"
196                          "<bold>{<i>n</i>}</bold> - The preceding item is matched exactly <i>n</i> times\n"
197                          "<bold>{<i>n</i>,}</bold> - The preceding item is matched <i>n</i> or more times\n"
198                          "<bold>{,<i>n</i>}</bold> - The preceding item is matched at most <i>n</i> times\n"
199                          "<bold>{<i>n</i>,<i>m</i>}</bold> - The preceding item is matched at least <i>n</i>,\n"
200                          "   but at most <i>m</i> times.\n"
201                          "\n"
202                          "Furthermore, backreferences to bracketed subexpressions are\n"
203                          "available via the notation \\\\<i>n</i>."
204                          ));
205     KQuickHelp::add(files_combo,
206                     i18n("Enter the file name pattern of the files to search here.\n"
207                          "You may give several patterns separated by commas"));
208     KQuickHelp::add(template_edit,
209                     i18n("You can choose a template for the pattern from the combo box\n"
210                          "and edit it here. The string %s in the template is replaced\n"
211                          "by the pattern input field, resulting in the regular expression\n"
212                          "to search for."));
213     KQuickHelp::add(resultbox,
214                     i18n("The results of the grep run are listed here. Select a\n"
215                          "filename/line number combination and press Enter or doubleclick\n"
216                          "on the item to show the respective line in the editor."));
217
218     connect( template_combo, SIGNAL(activated(int)),
219              SLOT(templateActivated(int)) );
220     connect( dir_button, SIGNAL(clicked()),
221              SLOT(dirButtonClicked()) );
222     connect( resultbox, SIGNAL(selected(const char *)),
223              SLOT(itemSelected(const char *)) );
224     connect( search_button, SIGNAL(clicked()),
225              SLOT(slotSearch()) );
226     connect( cancel_button, SIGNAL(clicked()),
227              SLOT(slotCancel()) );
228     connect( clear_button, SIGNAL(clicked()),
229              SLOT(slotClear()) );
230     connect( done_button, SIGNAL(clicked()),
231              SLOT(accept()) );
232 }
233
234
235 GrepDialog::~GrepDialog()
236 {
237     if (childproc)
238       delete childproc;
239 }
240
241
242 void GrepDialog::dirButtonClicked()
243 {
244     dir_edit->setText(KDirDialog::getDirectory(dir_edit->text()));
245 }
246
247
248 void GrepDialog::templateActivated(int index)
249 {
250     template_edit->setText(template_str[index]);
251 }
252
253
254 #include <iostream.h>
255 void GrepDialog::itemSelected(const char *item)
256 {
257   int pos;
258   QString filename, linenumber;
259   
260   QString str = item;
261   if ( (pos = str.find(':')) != -1)
262     {
263       filename = str.left(pos);
264       str = str.right(str.length()-1-pos);
265       if ( (pos = str.find(':')) != -1)
266         {
267           linenumber = str.left(pos);
268           emit itemSelected(filename,linenumber.toInt()-1);
269           //                cout << "Selected file " << filename << ", line " << linenumber << endl;
270         }
271     }
272 }
273
274
275 void GrepDialog::processOutput()
276 {
277   int pos;
278   while ( (pos = buf.find('\n')) != -1)
279     {
280       QString item = buf.left(pos);
281       if (!item.isEmpty())
282         resultbox->insertItem(item);
283       buf = buf.right(buf.length()-pos-1);
284     }
285   
286   QString str;
287   str.setNum(resultbox->count());
288   str += i18n(" matches");
289   matches_label->setText(str);
290 }
291
292
293 void GrepDialog::slotSearch()
294 {
295   search_button->setEnabled(false);
296   cancel_button->setEnabled(true);
297   
298   StringTokenizer tokener;
299   QString files;
300   QString files_temp = files_combo->currentText();
301   if (files_temp.right(1) != ","){ 
302     files_temp = files_temp + ",";
303   }
304   tokener.tokenize(files_temp,",");
305   if(tokener.hasMoreTokens()){
306     files = files + " '" + QString(tokener.nextToken())+"'" ;
307   }
308   while(tokener.hasMoreTokens()){
309     files = files + " -o -name " + "'"+QString(tokener.nextToken())+ "'";
310   }
311
312     status_label->setText(i18n("Searching..."));
313
314     QString pattern = template_edit->text();
315     pattern.replace(QRegExp("%s"), pattern_edit->text());
316     pattern.replace(QRegExp("'"), "'\\''");
317
318     QString filepattern = "`find '";
319     filepattern += dir_edit->text();
320     filepattern += "'";
321     if (!recursive_box->isChecked())
322         filepattern += " -maxdepth 1";
323     filepattern += " -name ";
324     filepattern += files;
325     filepattern += "`";
326
327     childproc = new KShellProcess("/bin/sh");
328     *childproc << "grep";
329     *childproc << "-n";
330     *childproc << (QString("-e '") + pattern + "'");
331     *childproc << filepattern;
332     *childproc << "/dev/null";
333
334     connect( childproc, SIGNAL(processExited(KProcess *)),
335              SLOT(childExited()) );
336     connect( childproc, SIGNAL(receivedStdout(KProcess *, char *, int)),
337              SLOT(receivedOutput(KProcess *, char *, int)) );
338     childproc->start(KProcess::NotifyOnExit, KProcess::Stdout);
339 }
340
341 void GrepDialog::slotSearchFor(QString pattern){
342                 slotClear();
343                 pattern_edit->clear();
344                 pattern_edit->setText(pattern);
345                 slotSearch();
346 }
347
348 void GrepDialog::finish()
349 {
350     search_button->setEnabled(true);
351     cancel_button->setEnabled(false);
352
353     buf += '\n';
354     processOutput();
355     if (childproc)
356       delete childproc;
357     childproc = 0;
358 }
359
360
361 void GrepDialog::slotCancel()
362 {
363     finish();
364
365     status_label->setText(i18n("Canceled"));
366 }
367
368
369 void GrepDialog::childExited()
370 {
371     int status = childproc->exitStatus();
372     
373     finish();
374
375     status_label->setText( (status == 1)? i18n("No matches found")
376                            : (status == 2)? i18n("Syntax error in pattern")
377                            : i18n("Ready") );
378     if (status != 0)
379         matches_label->setText("");
380     
381 }
382
383
384 void GrepDialog::receivedOutput(KProcess */*proc*/, char *buffer, int buflen)
385 {
386     buf += QString(buffer, buflen+1);
387     processOutput();
388 }
389
390
391 void GrepDialog::slotClear()
392 {
393     finish();
394     resultbox->clear();
395
396     status_label->setText(i18n("Ready"));
397     matches_label->setText("");
398 }
399
400
401 void  GrepDialog::setDirName(QString dir){
402   dir_edit->setText(dir);
403 }
404
405
406
407
408
409
410
411