source: s10k/CommonUtils/util.cpp@ 1076

Last change on this file since 1076 was 1073, checked in by s10k, 7 years ago

added XML Tools latest version (2.0d) and s10k's common libs

File size: 16.4 KB
Line 
1/**
2 * Copyright (C) 2017 - Fábio Bento (random-guy)
3 *
4 * This library is distributed under the MIT License. See notice at the end
5 * of this file.
6 *
7 */
8
9#include "util.h"
10
11namespace Util{
12
13namespace FileSystem {
14
15QString normalizePath(QString path){
16 return path.replace("\\","/");
17}
18
19QString cutName(QString path){
20 return path.remove(0,path.lastIndexOf('/')).remove('"');
21}
22
23QString cutNameWithoutBackSlash(QString path){
24 return cutName(path).remove('/');
25}
26
27QString normalizeAndQuote(QString path){
28 return String::insertQuotes(normalizePath(path));
29}
30
31// Created from scratch
32bool copyDir(const QString &fromPath, QString toPath, const bool isRecursive){
33 QDir fromDir(fromPath);
34 QDir toDir(toPath);
35
36 if(!toDir.mkdir(fromDir.dirName())){ // create the folder in the destination
37 return false;
38 }
39
40 // Update toPath to include the folder from "fromPath"
41 toPath = toPath + "/" + fromDir.dirName();
42 toDir = QDir(toPath);
43
44 for(const QFileInfo &currFileInfo : fromDir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot)){
45
46 if(currFileInfo.isFile()){
47
48 QFile destFile(toPath + "/" + currFileInfo.fileName());
49
50 if(!QFile::copy(currFileInfo.absoluteFilePath(),toPath + "/" + currFileInfo.fileName())){
51 return false;
52 }
53 }
54 else if(isRecursive && currFileInfo.isDir() && currFileInfo.absoluteFilePath() != fromDir.absolutePath()){
55
56 if(!copyDir(currFileInfo.absoluteFilePath(), toPath, isRecursive)){
57 return false;
58 }
59 }
60 }
61
62 return true;
63}
64
65//Copied from here: http://stackoverflow.com/questions/2536524/copy-directory-using-qt (ty roop)
66bool rmDir(const QString &dirPath)
67{
68 QDir dir(dirPath);
69 if (!dir.exists())
70 return true;
71 foreach(const QFileInfo &info, dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot)) {
72 if (info.isDir()) {
73 if (!rmDir(info.filePath()))
74 return false;
75 } else {
76 if (!dir.remove(info.fileName()))
77 return false;
78 }
79 }
80 QDir parentDir(QFileInfo(dirPath).path());
81 return parentDir.rmdir(QFileInfo(dirPath).fileName());
82}
83
84// Gets all files from a folder filtered by a given wildcard
85QStringList getFolderFilesByWildcard(const QString &entryFolder, const QString &wildcard, bool isRecursive){
86
87 QStringList filesFound; // result files with absolute path
88
89 QDirIterator it(entryFolder, QDir::Files, (isRecursive ? QDirIterator::Subdirectories : QDirIterator::NoIteratorFlags));
90
91 while (it.hasNext()){
92 filesFound << it.next();
93 }
94
95 return filterFilesByWildcard(filesFound, wildcard);
96}
97
98// Supports wildcards, and subdirectories with wildcard e.g.:
99// *.xml
100// /myXmls/*.xml
101//
102// online helper: https://regex101.com/
103QStringList filterFilesByWildcard(const QStringList &filePaths, const QString &wildcard){
104 QStringList resultFiles;
105 QString formattedWildcard;
106
107 if(wildcard.trimmed().isEmpty()){
108 return resultFiles;
109 }
110
111 formattedWildcard=normalizePath(wildcard); // Convert slashes to work in both mac and windows
112
113 // escape the string so '.' or '(' chars get correctly escaped
114 formattedWildcard = QRegularExpression::escape(formattedWildcard);
115
116 // replace * by the corresponding regex
117 formattedWildcard.replace("\\*",".*");
118
119 // replace ? by the corresponding regex
120 formattedWildcard.replace("\\?",".");
121
122 // if it doesn't start with any regex wildcard or a subdirectory slash, add a slash to beginning (so the file/folder matches at least the root folder)
123 // We use \\/ instead of / because it was escaped
124 if(!formattedWildcard.startsWith("\\/") && !formattedWildcard.startsWith(".*") && !formattedWildcard.startsWith(".")){
125 formattedWildcard = "\\/" + formattedWildcard;
126 }
127
128 // if it is a subdirectory add * to match
129 if(formattedWildcard.startsWith("\\/")){
130 formattedWildcard = ".*" + formattedWildcard;
131 }
132
133 formattedWildcard = "^" + formattedWildcard + "$"; // we want a full match (http://stackoverflow.com/a/5752852)
134
135 QRegularExpression regex(formattedWildcard);
136
137 for(const QString &currentFile : filePaths){
138
139 if(regex.match(currentFile).hasMatch()){
140 resultFiles << currentFile;
141 }
142
143 }
144
145 return resultFiles;
146}
147
148// Returns empty QString on failure.
149// Based from here: http://www.qtcentre.org/archive/index.php/t-35674.html (thanks wysota!)
150QString fileHash(const QString &fileName, QCryptographicHash::Algorithm hashAlgorithm)
151{
152
153 QCryptographicHash crypto(hashAlgorithm);
154 QFile file(fileName);
155 file.open(QFile::ReadOnly);
156 while(!file.atEnd()){
157 crypto.addData(file.read(8192));
158 }
159 QByteArray hash = crypto.result();
160
161 return QString(crypto.result().toHex());
162
163}
164
165
166/**
167 Gets application directory. In mac os gets the .app directory
168 **/
169QString getAppPath(){
170#ifdef Q_OS_MAC
171 QDir dir = QDir(QCoreApplication::applicationDirPath());
172 if(dir.absolutePath().contains(".app")){ // include bundle, but we don't want it
173 dir.cdUp();
174 dir.cdUp();
175 dir.cdUp();
176 }
177 return dir.absolutePath();
178#else
179 return QDir::currentPath();
180#endif
181}
182
183bool backupFile(QString file){
184 return QFile::copy(file,file+".bak");
185}
186
187}
188
189namespace String {
190
191QString insertApostrophes(const QString &currString){
192 return "'"+currString+"'";
193}
194
195QString insertQuotes(const QString &currString){
196 return "\""+currString+"\"";
197}
198
199QString fullTrim(QString str) {
200
201 str = str.simplified(); //convert all invisible chars in normal whitespaces
202 str.replace( " ", "" );
203
204 return str;
205}
206
207QStringList substring(QString myString, QString separator, Qt::CaseSensitivity cs){
208 QStringList result = QStringList();
209 int currIdx=0, nextIdx=0;
210
211 while(true){
212 nextIdx=myString.indexOf(separator,currIdx,cs);
213 result << myString.mid(currIdx,nextIdx-currIdx);
214 if(nextIdx==-1) break;
215 currIdx=nextIdx+1;
216 }
217
218 return result;
219}
220
221QString normalizeDecimalSeparator(QString value){
222 return value.replace(',','.');
223}
224
225//Searches for the QString "toSearch" in the "myString" variable backward
226//Returns the index of the first match or -1 if not found
227int indexOfBackward(QString myString, QString toSearch, int from){
228 int myStringSize=myString.size();
229 int toSearchSize=toSearch.size();
230
231 if(from==-1){
232 from=myStringSize;
233 }
234
235 int i=from;
236
237 while(i>=0){
238 for(int j=toSearchSize-1; j>=0; j--){
239 i--;
240 if(myString.at(i)!=toSearch.at(j)){
241 break;
242 }
243 if(j==0){
244 return i;
245 }
246 }
247 }
248
249 return -1;
250}
251
252// no problem here with "temporary" cstr
253// https://stackoverflow.com/questions/1971183/when-does-c-allocate-deallocate-string-literals
254const char* boolToCstr(bool currentBoolean){
255 return currentBoolean ? "true" : "false";
256}
257
258}
259
260#ifdef QT_GUI_LIB
261namespace Dialogs {
262
263void showInfo(const QString &message){
264 QMessageBox msgBox;
265 msgBox.setIcon(QMessageBox::Information);
266 msgBox.setText(message);
267 msgBox.exec();
268}
269
270void showRichInfo(const QString &message){
271 QMessageBox msgBox;
272 msgBox.setTextFormat(Qt::RichText);
273 msgBox.setIcon(QMessageBox::Information);
274 msgBox.setText(message);
275 msgBox.exec();
276}
277
278void showWarning(const QString &message){
279 QMessageBox msgBox;
280 msgBox.setIcon(QMessageBox::Warning);
281 msgBox.setText(message);
282 msgBox.exec();
283}
284
285void showError(const QString &message){
286 QMessageBox msgBox;
287 msgBox.setIcon(QMessageBox::Critical);
288 msgBox.setText(message);
289 msgBox.exec();
290}
291
292void showRichError(const QString &message){
293 QMessageBox msgBox;
294 msgBox.setIcon(QMessageBox::Critical);
295 msgBox.setText(message);
296 msgBox.exec();
297}
298
299bool showQuestion(QWidget * parent, QString message, QMessageBox::StandardButton standardButton){
300 return QMessageBox::question (parent, "Are you sure?", message, QMessageBox::Yes | QMessageBox::No, standardButton)==QMessageBox::Yes;
301}
302
303QMessageBox::StandardButton showQuestionWithCancel(QWidget * parent, QString message, QMessageBox::StandardButton standardButton){
304 return QMessageBox::question (parent, "Are you sure?", message, QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, standardButton);
305}
306
307QStringList multipleDirSelection(const QString &title){
308 QFileDialog w;
309
310 // We need to use non native dialog, because native doesn't support multiple folder selection
311 w.setOption(QFileDialog::DontUseNativeDialog,true);
312
313 w.setFileMode(QFileDialog::DirectoryOnly);
314
315 w.setWindowTitle(title);
316
317 QTreeView *t = w.findChild<QTreeView*>();
318
319 if (t) {
320 t->setSelectionMode(QAbstractItemView::MultiSelection);
321 }
322
323 if(w.exec()){ //if accepted
324 return w.selectedFiles();
325 }
326 return QStringList(); //return empty
327}
328
329}
330#endif
331
332namespace Validation {
333
334// Check if any string in the list is empty
335bool checkEmptySpaces(QStringList toCheck){
336 foreach (QString current, toCheck){
337 if(current.trimmed().isEmpty()){
338 return true; //There are empty spaces
339 }
340 }
341 return false;
342}
343
344bool checkIfIntegers(QStringList toCheck){
345 foreach (QString current, toCheck){
346 if(!isStringInteger(current)){
347 return true; // Some aren't valid integers
348 }
349 }
350 return false;
351}
352
353bool checkIfDoubles(QStringList toCheck){
354 foreach (QString current, toCheck){
355 if(!isStringDouble(current)){
356 return true; // Some aren't valid doubles
357 }
358 }
359 return false;
360}
361
362bool isStringInteger(QString myString){
363 bool isNumber;
364
365 myString.toInt(&isNumber); //convert to int and see if it succeeds
366
367 return isNumber;
368}
369
370bool isStringDouble(QString myString){
371 bool isDouble;
372
373 myString.toDouble(&isDouble); //convert to double and see if it succeeds
374
375 return isDouble;
376}
377
378}
379
380namespace System {
381
382#ifdef QT_GUI_LIB
383// From here: http://stackoverflow.com/questions/17893328/qt-getting-the-screen-resolution-without-the-extended-monitor ty Chris
384QRect getScreenResolution(){
385 QDesktopWidget widget;
386 return widget.availableGeometry(widget.primaryScreen()); // or screenGeometry(), depending on your needs
387}
388#endif
389
390}
391
392#ifdef QT_GUI_LIB
393namespace TableWidget {
394
395
396void addRow(QTableWidget *myTable, QStringList &columns){
397 //Get actual number rows
398 int twSize=myTable->rowCount();
399
400 //increase the rows for the new item
401 myTable->setRowCount(twSize+1);
402
403 //Add to table and list to
404 for(int i=0; i<columns.size(); i++){
405 QTableWidgetItem *newColumn = new QTableWidgetItem(columns[i]);
406 myTable->setItem(twSize,i,newColumn);
407 // Add a tooltip with with the cell content
408 myTable->item(twSize,i)->setToolTip(myTable->item(twSize,i)->text());
409 }
410
411}
412
413
414QModelIndexList getSelectedRows(QTableWidget *myTable){
415 return myTable->selectionModel()->selectedRows();
416}
417
418QModelIndexList getCurrentRows(QTableWidget *myTable){
419
420 QModelIndexList oldSelection = getSelectedRows(myTable);
421
422 myTable->selectAll();
423
424 QModelIndexList allRows = getSelectedRows(myTable);
425
426 myTable->selectionModel()->clearSelection();
427
428 // Restore old selection
429 for(const QModelIndex &currentIndex : oldSelection){
430 myTable->selectionModel()->select(currentIndex, QItemSelectionModel::Select | QItemSelectionModel::SelectionFlag::Rows);
431 }
432
433 return allRows;
434}
435
436int getNumberSelectedRows(QTableWidget *myTable){
437 return getSelectedRows(myTable).size();
438}
439
440void clearContents(QTableWidget *myTable, const QString &nothingToClearMessage, const QString &questionToClear){
441
442 if(myTable->rowCount()==0){
443 Dialogs::showInfo(nothingToClearMessage);
444 return;
445 }
446
447 if(Dialogs::showQuestion(myTable, questionToClear)){
448 clearContentsNoPrompt(myTable);
449 }
450
451}
452
453void clearContentsNoPrompt(QTableWidget *myTable){
454 myTable->clearContents();
455 myTable->setRowCount(0);
456}
457
458// Adapted from here:
459// http://stackoverflow.com/questions/29176317/qtablewidget-checkbox-get-state-and-location
460void addCheckBox(QTableWidget *myTable, int row, int column, QCheckBox *checkbox){
461 if(checkbox == nullptr){
462 checkbox = new QCheckBox();
463 }
464
465 QWidget *auxLayoutWidget = new QWidget(myTable);
466
467 QHBoxLayout* checkBoxLayout = new QHBoxLayout();
468 checkBoxLayout->setContentsMargins(0,0,0,0);
469 checkBoxLayout->addWidget(checkbox);
470 checkBoxLayout->setAlignment(Qt::AlignCenter);
471 checkBoxLayout->setSpacing(0);
472 auxLayoutWidget->setLayout(checkBoxLayout);
473
474 myTable->setCellWidget(row, column, auxLayoutWidget);
475}
476
477// Adapted from here:
478// http://stackoverflow.com/questions/29176317/qtablewidget-checkbox-get-state-and-location
479QCheckBox* getCheckBoxFromCell(QTableWidget *myTable, int row, int column){
480 return dynamic_cast<QCheckBox*>(myTable->cellWidget(row, column)->findChild<QCheckBox *>());
481}
482
483// Adapted from here:
484// http://www.qtcentre.org/threads/3386-QTableWidget-move-row
485// Thanks jpn
486void swapRows(QTableWidget *myTable, const int indexSourceRow, const int indexDestinationRow, bool selectSwappedRow)
487{
488 // takes and returns the whole row
489 auto takeRow = [&myTable](int row) -> QList<QTableWidgetItem*>
490 {
491 QList<QTableWidgetItem*> rowItems;
492 for (int col = 0; col < myTable->columnCount(); ++col)
493 {
494 rowItems << myTable->takeItem(row, col);
495 }
496 return rowItems;
497 };
498
499 // sets the whole row
500 auto setRow = [&myTable](int row, const QList<QTableWidgetItem*>& rowItems)
501 {
502 for (int col = 0; col < myTable->columnCount(); ++col)
503 {
504 myTable->setItem(row, col, rowItems.at(col));
505 }
506 };
507
508 // take whole rows
509 QList<QTableWidgetItem*> sourceItems = takeRow(indexSourceRow);
510 QList<QTableWidgetItem*> destItems = takeRow(indexDestinationRow);
511
512 // set back in reverse order
513 setRow(indexSourceRow, destItems);
514 setRow(indexDestinationRow, sourceItems);
515
516 if(selectSwappedRow){
517 myTable->selectRow(indexDestinationRow);
518 }
519}
520
521void deleteSelectedRows(QTableWidget *myTable){
522 int size = myTable->selectionModel()->selectedRows().size();
523
524 for(int i=0; i<size; i++){
525 myTable->removeRow(myTable->selectionModel()->selectedRows().at(size-i-1).row());
526 }
527}
528
529}
530#endif
531
532#ifdef QT_GUI_LIB
533namespace StatusBar {
534
535void showError(QStatusBar * const statusBar, const QString &message){
536
537 QPalette myPalete = QPalette();
538 myPalete.setColor( QPalette::WindowText, QColor(255,0,0));
539 statusBar->setPalette( myPalete );
540 statusBar->showMessage(message,10000); //display by 10 seconds
541
542}
543
544void showSuccess(QStatusBar * const statusBar,const QString &message){
545
546 QPalette myPalete = QPalette();
547 myPalete.setColor( QPalette::WindowText, QColor(0,150,0));
548 statusBar->setPalette( myPalete );
549 statusBar->showMessage(message,10000); //display by 10 seconds
550
551}
552
553}
554#endif
555
556}
557
558
559/**
560 * Copyright (c) 2017 - Fábio Bento (random-guy)
561 *
562 * Permission is hereby granted, free of charge, to any person
563 * obtaining a copy of this software and associated documentation
564 * files (the "Software"), to deal in the Software without
565 * restriction, including without limitation the rights to use,
566 * copy, modify, merge, publish, distribute, sublicense, and/or sell
567 * copies of the Software, and to permit persons to whom the
568 * Software is furnished to do so, subject to the following
569 * conditions:
570 *
571 * The above copyright notice and this permission notice shall be
572 * included in all copies or substantial portions of the Software.
573 *
574 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
575 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
576 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
577 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
578 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
579 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
580 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
581 * OTHER DEALINGS IN THE SOFTWARE.
582 */
Note: See TracBrowser for help on using the repository browser.