Salome HOME
Join modifications from branch BR_DEBUG_3_2_0b1
[modules/gui.git] / src / Qtx / QtxDirListEditor.cxx
1 // Copyright (C) 2005  OPEN CASCADE, CEA/DEN, EDF R&D, PRINCIPIA R&D
2 // 
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either 
6 // version 2.1 of the License.
7 // 
8 // This library is distributed in the hope that it will be useful 
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public  
14 // License along with this library; if not, write to the Free Software 
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19 #include "QtxDirListEditor.h"
20
21 #include <qlabel.h>
22 #include <qlayout.h>
23 #include <qpushbutton.h>
24 #include <qmessagebox.h>
25 #include <qfiledialog.h>
26 #include <qapplication.h>
27
28 #define MARGIN_SIZE    11
29 #define SPACING_SIZE    6
30 #define SPACER_SIZE     5
31
32 static const char* delete_icon[] = {
33 "16 16 3 1",
34 "` c #810000",
35 "  c none",
36 "# c #ffffff",
37 "                ",
38 "                ",
39 " ``#        ``# ",
40 " ````#     ``#  ",
41 "  ````#   ``#   ",
42 "    ```# `#     ",
43 "     `````#     ",
44 "      ```#      ",
45 "     `````#     ",
46 "    ```# ``#    ",
47 "   ```#   ``#   ",
48 "  ```#     `#   ",
49 "  ```#      `#  ",
50 "   `#        `# ",
51 "                ",
52 "                "
53 };
54
55 static const char* insert_icon[] = {
56 "16 16 5 1",
57 "` c #000000",
58 ". c #ffff00",
59 "# c #9d9da1",
60 "  c none",
61 "b c #ffffff",
62 "                ",
63 "                ",
64 " #  #b #.       ",
65 "  # #.#.` ` `   ",
66 "  .#.b####   `  ",
67 " ### ..         ",
68 "  . # .#     `  ",
69 " #` #.          ",
70 "    #        `  ",
71 "  `             ",
72 "             `  ",
73 "  `             ",
74 "             `  ",
75 "  ` ` ` ` ` `   ",
76 "                ",
77 "                "
78 };
79
80 static const char* movedown_icon[] = {
81 "16 16 2 1",
82 "` c #000000",
83 "  c none",
84 "                ",
85 "                ",
86 "         ```    ",
87 "        ```     ",
88 "       ```      ",
89 "       ```      ",
90 "       ```      ",
91 "       ```      ",
92 "   ```````````  ",
93 "    `````````   ",
94 "     ```````    ",
95 "      `````     ",
96 "       ```      ",
97 "        `       ",
98 "                ",
99 "                "
100 };
101
102 static const char* moveup_icon[] = {
103 "16 16 2 1",
104 "` c #000000",
105 "  c none",
106 "                ",
107 "                ",
108 "        `       ",
109 "       ```      ",
110 "      `````     ",
111 "     ```````    ",
112 "    `````````   ",
113 "   ```````````  ",
114 "       ```      ",
115 "       ```      ",
116 "       ```      ",
117 "       ```      ",
118 "      ```       ",
119 "     ```        ",
120 "                ",
121 "                "
122 };
123
124 /*!
125   Constructor
126 */
127 QtxDirListEditor::QtxDirListEditor( QWidget* parent )
128 : QWidget( parent )
129 {
130   myEdited       = false;
131   myLastSelected = 0;
132   myEdit         = 0; 
133   myBtn          = 0;
134
135   QGridLayout* topLayout = new QGridLayout(this);
136   topLayout->setMargin(0);
137   topLayout->setSpacing(0);
138   
139   setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ) );
140   
141   myDirList = new QListBox(this);
142   myDirList->setSelectionMode(QListBox::Single);
143   myDirList->setHScrollBarMode(QListBox::AlwaysOff);
144   myDirList->horizontalScrollBar()->installEventFilter(this);
145   myDirList->verticalScrollBar()->installEventFilter(this);
146   myDirList->insertItem(tr("<empty>"));
147   myDirList->installEventFilter(this);
148
149   QHBoxLayout* ctrlLayout = new QHBoxLayout;
150   ctrlLayout->setMargin(0);
151   ctrlLayout->setSpacing(0);
152
153   // QLabel* lab = new QLabel(myDirList, tr("DIRECTORIES_LBL"), this);
154
155   QToolButton* insertBtn = new QToolButton(this);
156   insertBtn->setIconSet(QPixmap( insert_icon ));
157   insertBtn->setAutoRaise(true);
158
159   QToolButton* deleteBtn = new QToolButton(this);
160   deleteBtn->setIconSet(QPixmap( delete_icon ));
161   deleteBtn->setAutoRaise(true);
162
163   QToolButton* upBtn = new QToolButton(this);
164   upBtn->setIconSet(QPixmap( moveup_icon ));
165   upBtn->setAutoRaise(true);
166
167   QToolButton* downBtn = new QToolButton(this);
168   downBtn->setIconSet(QPixmap( movedown_icon ));
169   downBtn->setAutoRaise(true);
170   
171   // ctrlLayout->addWidget(lab);
172   ctrlLayout->addItem(new QSpacerItem(SPACER_SIZE, SPACER_SIZE, QSizePolicy::Expanding, QSizePolicy::Minimum));
173   ctrlLayout->addWidget(insertBtn);
174   ctrlLayout->addWidget(deleteBtn);
175   ctrlLayout->addWidget(upBtn);
176   ctrlLayout->addWidget(downBtn);
177
178   QHBoxLayout* btnLayout = new QHBoxLayout;
179   btnLayout->setMargin(0);
180   btnLayout->setSpacing(6);
181   
182   topLayout->addLayout(ctrlLayout, 0, 0);
183   topLayout->addWidget(myDirList,  1, 0);
184   topLayout->addLayout(btnLayout,  2, 0);
185
186   connect(myDirList, SIGNAL(mouseButtonClicked(int, QListBoxItem*, const QPoint&)), 
187           this, SLOT(onMouseButtonClicked(int, QListBoxItem*, const QPoint&)));
188   connect(myDirList, SIGNAL(doubleClicked(QListBoxItem*)), 
189           this, SLOT(onDblClicked(QListBoxItem*)));
190   
191   connect(insertBtn, SIGNAL(clicked()), this, SLOT(onInsert()));
192   connect(deleteBtn, SIGNAL(clicked()), this, SLOT(onDelete()));
193   connect(upBtn,     SIGNAL(clicked()), this, SLOT(onUp()));
194   connect(downBtn,   SIGNAL(clicked()), this, SLOT(onDown()));
195 }
196
197 /*!
198   Destructor
199 */
200 QtxDirListEditor::~QtxDirListEditor()
201 {
202 }
203
204 /*!
205   Gets list of paths
206 */
207 void QtxDirListEditor::getPathList(QStringList& list)
208 {
209   // Finish the path editing
210   if (myEdit) {
211     validate(true);
212     
213     myEdit->deleteLater();
214     myBtn->deleteLater();
215     myEdit = 0;
216     myBtn  = 0;
217     myEdited = false;
218     myDirList->setFocus();
219   }
220
221   list.clear();
222   for (unsigned i = 0; i < myDirList->count()-1; i++)
223     list.append(myDirList->text(i));
224 }
225
226 /*!
227   Sets list of paths
228 */
229 void QtxDirListEditor::setPathList(const QStringList& list) {
230   myDirList->clear();
231   myDirList->insertItem(tr("<empty>"));
232   for (unsigned i = 0; i < list.count(); i++)
233     myDirList->insertItem(list[i], myDirList->count()-1);
234 }
235
236 /*!
237   Validates entered path, returns true if OK
238 */
239 bool QtxDirListEditor::validate( const bool quietMode )
240 {
241   if ( myEdited )
242   {
243     QString dirPath = QFileInfo( myEdit->text().stripWhiteSpace() ).filePath();
244 /*
245 #ifndef WNT
246     if ( dirPath.startsWith( "~") ) {
247       dirPath = dirPath.remove(0,1);
248       QString user;
249       int slashPos = dirPath.find("/");
250       if ( slashPos >= 0 ) {
251         user = dirPath.left(slashPos);
252         dirPath = dirPath.mid(slashPos);
253       }
254       else {
255         user = dirPath;
256         dirPath = "";
257       }
258       if ( user.isEmpty() )
259         user = getenv( "USER" );
260
261       struct passwd* user_data = getpwnam( user.latin1() );
262       if ( user_data == NULL ) {
263         // unknown user or something another error
264         QMessageBox::critical(this, 
265                               tr("Error"),
266                               tr("Unknown user %1").arg(user), 
267                               tr("Ok"));
268         myEdit->setFocus();
269         return false;
270       }
271       dirPath = user_data->pw_dir + dirPath;
272     }
273 #endif
274 */
275     QDir dir(dirPath);
276     QListBoxItem* found = 0;
277     for (unsigned i = 0; i < myDirList->count()-1; i++) {
278       QDir aDir(myDirList->text(i));
279       if ( aDir.canonicalPath().isNull() && myDirList->text(i) == dir.absPath() ||
280           !aDir.canonicalPath().isNull() && aDir.exists() && aDir.canonicalPath() == dir.canonicalPath()) {
281           found = myDirList->item(i);
282         break;
283       }
284     }
285     if (dirPath.isEmpty()) {
286       if (found) {
287         // it should be last (empty) item in the list - nothing to do
288         return true;
289       }
290       else {
291         // delete directory from the list
292         removeDir(myLastSelected);
293         return true;
294       }
295     }
296     else {
297       if (found) {
298         if (found != myLastSelected) {
299           // it is forbidden to add directory more then once
300           if ( !quietMode )
301             QMessageBox::critical(this, 
302                                   tr("Error"),
303                                   tr("Directory already specified."), 
304                                   tr("Ok"));
305           myEdit->setFocus();
306           return false;
307         }
308       }
309       else {
310         if (!dir.exists()) {
311           if ( !quietMode && QMessageBox::information(this, 
312                                                       tr("Warning"),
313                                                       tr("%1\n\nThe directory doesn't exist.\nAdd directory anyway?").arg(dir.absPath()),
314                                                       tr("Yes"), tr("No"), QString::null, 1, 1) == 1) {
315             myEdit->setFocus();
316             return false;
317           }
318         }
319         // append
320         appendDir(myLastSelected, dir.absPath());
321       }
322     }
323   }
324   return true;
325 }
326
327 /*!
328   Appends/changes directory
329 */
330 void QtxDirListEditor::appendDir(QListBoxItem* item, const QString& dir) {
331   int index = myDirList->index(item);
332   if (index >= 0 && index < (int)myDirList->count()) {
333     if (index == (int)myDirList->count()-1) {
334       // it is the last item (new), well, insert it before the last (empty)
335       myDirList->insertItem(dir, myDirList->count()-1);
336     }
337     else {
338       // change item
339       myDirList->changeItem(dir, index);
340     }
341   }
342 }
343
344 /*!
345   Removes directory from list
346 */
347 void QtxDirListEditor::removeDir(QListBoxItem* item) {
348   // do not remove last item (empty)
349   int index = myDirList->index(item);
350   if (index >= 0 && index < (int)myDirList->count()-1) {
351     delete item;
352     myLastSelected = myDirList->item(index);
353     myDirList->setSelected(myLastSelected, true);
354   }
355 }
356
357 /*!
358   Resize event
359 */
360 void QtxDirListEditor::resizeEvent(QResizeEvent* event) {
361   QWidget::resizeEvent(event);
362   if ( myEdited ) {
363     myEdit->resize(myDirList->viewport()->width()-myBtn->sizeHint().width(), myEdit->height());
364     myBtn->move(myEdit->width(), myEdit->y());
365   }
366 }
367
368 /*!
369   Called when user clicks inside directories list box
370 */
371 void QtxDirListEditor::onMouseButtonClicked(int           button, 
372                                             QListBoxItem* item, 
373                                             const QPoint& point) {
374   if (myEdited) {
375     if (!validate()) {
376       myDirList->setCurrentItem(myLastSelected);
377       myDirList->setSelected(myLastSelected, true);
378       return;
379     }
380     delete myEdit;
381     delete myBtn;
382     myEdit = 0;
383     myBtn  = 0;
384     myEdited = false;
385     myDirList->setFocus();
386   }
387   if (item) {
388     myDirList->setCurrentItem(item);
389     myDirList->setSelected(item, true);
390     myDirList->ensureCurrentVisible();
391     qApp->processEvents();
392     if (button == LeftButton && myLastSelected == item) {
393       QRect ir = myDirList->itemRect(myLastSelected);
394       
395       myEdit = new QLineEdit(myDirList->viewport());
396       myBtn  = new QToolButton(myDirList->viewport());
397       myBtn->setText(" ... ");
398       connect(myBtn, SIGNAL(clicked()), this, SLOT(onBtnClicked()));
399       myEdit->setGeometry(0, 
400                           ir.top()-(myEdit->sizeHint().height()-ir.height())/2, 
401                           myDirList->viewport()->width()-myBtn->sizeHint().width(), 
402                           myEdit->sizeHint().height());
403       myBtn->setGeometry (myEdit->width(), 
404                           ir.top()-(myEdit->sizeHint().height()-ir.height())/2, 
405                           myBtn->sizeHint().width(),
406                           myEdit->sizeHint().height());
407       connect(myEdit, SIGNAL(returnPressed()), this, SLOT(onEditFinished()));
408       myEdited = true;
409       myEdit->show();
410       myBtn->show();
411       if (myDirList->index(myLastSelected) != (int)myDirList->count()-1)
412         myEdit->setText(myLastSelected->text());
413       myEdit->selectAll();
414       myEdit->setCursorPosition(myEdit->text().length());
415       myEdit->installEventFilter(this);
416       myEdit->setFocus();
417     }
418   }
419   else {
420     myDirList->clearSelection();
421   }
422   myLastSelected = item;
423 }
424
425 /*!
426   Called when user double-clicks on any item
427 */
428 void QtxDirListEditor::onDblClicked(QListBoxItem* item) {
429   onMouseButtonClicked(LeftButton, item, QPoint(0,0));
430 }
431
432 /*!
433   <...> (Browse dir) button slot
434 */
435 void QtxDirListEditor::onBtnClicked() {
436   QString dir = myEdit->text().stripWhiteSpace().isEmpty() ? 
437                 QString::null : 
438                 myEdit->text().stripWhiteSpace();
439
440   dir = QFileDialog::getExistingDirectory(dir, this, 0, tr("Select directory"), true);
441
442   if (!dir.isEmpty()) {
443     myEdit->setText(dir);
444     myEdit->selectAll();
445     myEdit->setCursorPosition(myEdit->text().length());
446   }
447 }
448
449 /*!
450   Called when user finises editing of path by pressing <Enter>
451 */
452 void QtxDirListEditor::onEditFinished() {
453   if (myEdit) {
454     if (!validate()) {
455       myDirList->setCurrentItem(myLastSelected);
456       myDirList->setSelected(myLastSelected, true);
457       return;
458     }
459     myEdit->deleteLater();
460     myBtn->deleteLater();
461     myEdit = 0;
462     myBtn  = 0;
463     myEdited = false;
464     myDirList->setFocus();
465   }
466 }
467
468 /*!
469   Event filter
470 */
471 bool QtxDirListEditor::eventFilter(QObject* object, QEvent* event) {
472   if ( myEdited ) {
473     if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick) {
474       if (object == myDirList->horizontalScrollBar() || object == myDirList->verticalScrollBar()) {
475         if (!validate()) {
476           myDirList->setCurrentItem(myLastSelected);
477           myDirList->setSelected(myLastSelected, true);
478           return true;
479         }
480         delete myEdit;
481         delete myBtn;
482         myEdit = 0;
483         myBtn  = 0;
484         myEdited = false;
485         myDirList->setFocus();
486       }
487     }
488     else if (event->type() == QEvent::KeyPress) {
489       QKeyEvent* ke = (QKeyEvent*)event;
490       if (ke->key() == Key_Tab)
491         return true;
492       if (object == myDirList) {
493         return true;
494       }
495       else if (object == myEdit) {
496         if ( ke->key() == Key_Up || ke->key() == Key_Down || ke->key() == Key_PageUp || ke->key() == Key_PageDown ||
497              ( ke->key() == Key_Home  || ke->key() == Key_End  || ke->key() == Key_Prior || ke->key() == Key_Next ) && 
498                  (ke->state() & ControlButton) ) {
499           return true;
500         }
501         else if ( ke->key() == Key_Escape ) {
502           delete myEdit;
503           delete myBtn;
504           myEdit = 0;
505           myBtn  = 0;
506           myEdited = false;
507           myDirList->setFocus();
508           return true;
509         }
510       }
511     }
512   }
513   return QWidget::eventFilter(object, event);
514 }
515
516 /*!
517   <Insert> button slot
518 */
519 void QtxDirListEditor::onInsert() {
520   if (!myEdited) {
521     myLastSelected = 0;
522     onMouseButtonClicked(LeftButton, myDirList->item(myDirList->count()-1), QPoint(0,0));
523     onMouseButtonClicked(LeftButton, myDirList->item(myDirList->count()-1), QPoint(0,0));
524   }
525 }
526
527 /*!
528   <Delete> button slot
529 */
530 void QtxDirListEditor::onDelete() {
531   if (!myEdited && myDirList->currentItem() >=0) {
532     removeDir(myDirList->item(myDirList->currentItem()));
533     myDirList->setFocus();
534   }
535 }
536
537 /*!
538   <Move up> button slot
539 */
540 void QtxDirListEditor::onUp() {
541   if (!myEdited && myLastSelected) {
542     int index = myDirList->currentItem();
543     if (index > 0 && index < (int)myDirList->count()-1 && myDirList->isSelected(index)) {
544       QString t = myDirList->text(index-1);
545       myDirList->changeItem(myDirList->text(index), index-1);
546       myDirList->changeItem(t, index);
547       myDirList->setCurrentItem(index-1);
548       myLastSelected = myDirList->item(index-1);
549       myDirList->setSelected(myLastSelected, true);
550       myDirList->setFocus();
551     }
552   }
553 }
554
555 /*!
556   <Move down> button slot
557 */
558 void QtxDirListEditor::onDown() {
559   if (!myEdited && myLastSelected) {
560     int index = myDirList->currentItem();
561     if (index >= 0 && index < (int)myDirList->count()-2 && myDirList->isSelected(index)) {
562       QString t = myDirList->text(index+1);
563       myDirList->changeItem(myDirList->text(index), index+1);
564       myDirList->changeItem(t, index);
565       myDirList->setCurrentItem(index+1);
566       myLastSelected = myDirList->item(index+1);
567       myDirList->setSelected(myLastSelected, true);
568       myDirList->setFocus();
569     }
570   }
571 }