aboutsummaryrefslogtreecommitdiff
path: root/lumina-fileinfo/dialog.cpp
blob: fe6e69c3b3cc655daaba7edc66a31da4c7fa3fc8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
#include "dialog.h"
#include "ui_dialog.h"
#include <QFileDialog>
#include <QRegExp>
#include <QTemporaryFile>
#include <QMessageBox>
#include <QImageReader>
#include "LuminaUtils.h"
#include <LuminaOS.h>


//this function is just like a regexp.
//we just change the required lines and we don't touch to the rest of the file and copy it back.
void Dialog::textReplace(QString &origin, QString from, QString to, QString topic)
{
  if (origin.contains(QRegExp("\n" + topic + "\\s*=\\s*" + from + "\n",Qt::CaseInsensitive))) {
    origin.replace(QRegExp("\n" + topic + "\\s*=\\s*" + from + "\n",Qt::CaseInsensitive),"\n" + topic + "=" + to + "\n");
  } else {
    origin.append(topic + "=" + to + "\n");
  }
}

//get the template from the user home directory or from the qrc files
void Dialog::copyTemplate(QString templateType)   
{
  if ((templateType == "-link") or (templateType == "-app")) {
    if (QFile::exists(QDir::homePath() + "/.lumina/LuminaDE/fileinfo" + templateType + ".template")) {
      //We take the template from homedir
      QFile::copy(QDir::homePath() + "/.lumina/LuminaDE/fileinfo" + templateType + ".template", desktopFileName);
    } else {
      //last possibility is to use the qrc template. 
      //But based on the initialisation, this should never occurs
      QFile::copy(":defaults/fileinfo" + templateType + ".template", desktopFileName);
    }
  } else {
    //error message for developpers
    qDebug() << "copyTemplate only accept '-link' or '-app' as parameter";
  }
}




Dialog::Dialog(QWidget *parent) :
      QDialog(parent),
      ui(new Ui::Dialog)
{
  ui->setupUi(this);
  desktopType="Application"; //default value

  //Setup all the icons using libLumina
  setupIcons();

  //we copy qrc templates in the home dir of the user. 
  //this allow the user to adapt those template to is own whishes
  QString templateFile = QDir::homePath() + "/.lumina/LuminaDE/fileinfo-link.template";
  if (!QFile::exists(templateFile)) {
    QFile::copy(":defaults/fileinfo-link.template", templateFile);
    QFile(templateFile).setPermissions(QFileDevice::ReadUser|QFileDevice::WriteUser);
  }
  templateFile = QDir::homePath() + "/.lumina/LuminaDE/fileinfo-app.template";
  if (!QFile::exists(templateFile)) {
    QFile::copy(":defaults/fileinfo-app.template", templateFile);
    QFile(templateFile).setPermissions(QFileDevice::ReadUser|QFileDevice::WriteUser);
  }
}


Dialog::~Dialog()
{
  delete ui;
}

void Dialog::setupIcons(){   
  this->setWindowIcon( LXDG::findIcon("unknown","") );
  ui->pbWorkingDir->setIcon( LXDG::findIcon("folder","") );
  ui->pbCommand->setIcon( LXDG::findIcon("system-search","") );
}

//Inform the user that required input parameters are missing
void Dialog::MissingInputs()
{
  qDebug() << "We cannot continue without a desktop file !!!";
  QMessageBox::critical(this, tr("Error"), tr("Lumina-fileinfo requires inputs:")+"\n"+QString(tr("Example: \"%1\"")).arg("lumina-fileinfo <-application>|<-link> desktopfile") );
  exit(1);
}


//Initialise the layout of the screen.
void Dialog::Initialise(QString param)
{
  //in case of "link", several objects are no required
  if (param.startsWith("-link")) {
    ui->cbRunInTerminal->setVisible(false);
    ui->cbStartupNotification->setVisible(false);
    ui->lCommand->setVisible(false);
    ui->pbCommand->setVisible(false);
    ui->lblCommand->setVisible(false);
    ui->lblOptions->setVisible(false);
    ui->lblWorkingDir->setText(tr("URL")); //we use the WorkingDir boxes for URL
    desktopType="link";
  }
  if (param.startsWith("-app")) {
    ui->cbRunInTerminal->setVisible(true);
    ui->cbStartupNotification->setVisible(true);
    ui->lCommand->setVisible(true);
    ui->pbCommand->setVisible(true);
    ui->lblCommand->setVisible(true);
    ui->lblOptions->setVisible(true);
    ui->lblWorkingDir->setText(tr("Working dir"));
    desktopType="app";
  }
}

//load the desktop file or the required template
void Dialog::LoadDesktopFile(QString input)
{
  //if we have "-" as 1st char, it means that this is not a desktop file, but a parameter
  desktopFileName = input;
  if (input.startsWith("-")) {
    QMessageBox::critical(this,tr("Error"),tr("The filename cannot start with a \"-\"."));
    exit(1);
  }
  //if proposed file does not exist, than we will create one based on the templates
  QFileInfo info(desktopFileName);
  if ((info.size() == 0) && (desktopFileName.endsWith(".desktop"))) {
    QFile::remove(desktopFileName); //for the copy, we need to remove it
    if (desktopType=="link") {
      copyTemplate("-link");
    } else {
      copyTemplate("-app");
    }
  }
  this->setWindowTitle(desktopFileName.section("/",-1));
  ui->tabWidget->setCurrentIndex(0); //always start on the file info tab
  //Now load the file info and put it into the UI
  QString mime = LXDG::findAppMimeForFile(desktopFileName);
  QList<QByteArray> fmt = QImageReader::supportedImageFormats();
  bool foundimage=false;
    for(int i=0; i<fmt.length(); i++){ 
      if(info.suffix().toLower() == QString(fmt[i]).toLower()){foundimage=true; break; }
    }
  if(foundimage){
    ui->label_file_icon->setPixmap( QPixmap(desktopFileName).scaledToHeight(64) );
  }else{
    ui->label_file_icon->setPixmap( LXDG::findMimeIcon(mime).pixmap(QSize(64,64)) );	  
  }
  ui->label_file_mimetype->setText(mime);
  QString type;
  if(desktopFileName.endsWith(".desktop")){ type = tr("XDG Shortcut"); }
  else if(info.isDir()){ 
    type = tr("Directory");
    ui->label_file_mimetype->setText("inode/directory");
    ui->label_file_icon->setPixmap( LXDG::findIcon("folder","").pixmap(QSize(64,64)) );
  }else if(info.isExecutable()){ type = tr("Binary"); }
  else{ type = info.suffix().toUpper(); }
  if(info.isHidden()){ type = QString(tr("Hidden %1")).arg(type); }
  ui->label_file_type->setText(type);
  //Calculate the size of the file/dir
  QString sz;
  if(!info.isDir()){
    sz = LUtils::BytesToDisplaySize(info.size());
  }
  // TO-DO calculate the total size of all files within this directory (in a background thread)
  ui->label_file_size->setText( sz );
  ui->label_file_owner->setText(info.owner());
  ui->label_file_group->setText(info.group());
  QString perms;
  if(info.isReadable() && info.isWritable()){ perms = tr("Read/Write"); }
  else if(info.isReadable()){ perms = tr("Read Only"); }
  else if(info.isWritable()){ perms = tr("Write Only"); }
  else{ perms = tr("No Access"); }
  ui->label_file_perms->setText(perms);
  ui->label_file_created->setText( info.created().toString(Qt::SystemLocaleLongDate) );
  ui->label_file_modified->setText( info.lastModified().toString(Qt::SystemLocaleLongDate) );
  
  //use the standard LXDG object and load the desktop file
  bool ok = false;
  if(desktopFileName.endsWith(".desktop")){
    DF = LXDG::loadDesktopFile(desktopFileName, ok);
  }
  if( ok ) {
    if ((DF.type == XDGDesktop::LINK) && (desktopType!="link" )) {
      //we open a desktop type "link" but it was not mentionned by parameters
        Dialog::Initialise("-link");
    }
    ui->lName->setText(DF.name);
    ui->lComment->setText(DF.comment);
    ui->lCommand->setText(DF.exec);
    //in case of "link" desktop, we populate the correct content in lWorkingDir
    if (desktopType=="link") {
      ui->lWorkingDir->setText(DF.url);
    } else {
      ui->lWorkingDir->setText(DF.path);
    }
    if (DF.startupNotify) ui->cbStartupNotification->setChecked(true); else ui->cbStartupNotification->setChecked(false);
    if (DF.useTerminal) ui->cbRunInTerminal->setChecked(true); else ui->cbRunInTerminal->setChecked(false);
    iconFileName="";
    ui->pbIcon->setIcon(LXDG::findIcon(DF.icon,""));
    if(!info.isWritable()){ ui->tab_deskedit->setEnabled(false); }
  } else {
    ui->tabWidget->removeTab(1);
    return;
  }

  //we load the file in memory and will adapt it before saving it to disk
  QFile file(desktopFileName);
  inMemoryFile="";
  if (file.open(QFile::ReadOnly)) {
    QTextStream fileData(&file);
    inMemoryFile = fileData.readAll();
    file.close();
    //perform some validation checks
    //this will allow checks improvements without compilation of the file
    if ((inMemoryFile.contains(QRegExp(".*\\[Desktop Entry\\].*\n"))) &&
      (inMemoryFile.contains(QRegExp("\n\\s*Type\\s*=.*\n"))) &&
      (inMemoryFile.contains(QRegExp("\n\\s*Name\\s*=.*\n")))) {
      //qDebug() << "sounds a good file"; 
    } else {
      //qDebug() << "wrong file!!!!";
      QMessageBox msgBox;
      msgBox.setIcon(QMessageBox::Question);
      msgBox.setText(tr("There are some issues with this file !!!!"));
      msgBox.setInformativeText(tr("Either you correct this file your self with an editor, or you start from scratch using the link or app template.\nPlease note that this process will update the file called:") + desktopFileName);
      QPushButton *linkButton = msgBox.addButton("Link",QMessageBox::AcceptRole);
      QPushButton *appButton = msgBox.addButton("App",QMessageBox::ResetRole);
      QPushButton *cancelButton = msgBox.addButton("Cancel",QMessageBox::NoRole);
      msgBox.exec();
      if (msgBox.clickedButton() == linkButton) {
        QFile::remove(desktopFileName);
        copyTemplate("-link");
        Initialise("-link");
        LoadDesktopFile(desktopFileName);
      }
      if (msgBox.clickedButton() == appButton) {
        QFile::remove(desktopFileName);
        copyTemplate("-app");
        Initialise("-app");
        LoadDesktopFile(desktopFileName);
      }
      if (msgBox.clickedButton() == cancelButton) {
        //we stop here
        exit(0);
      }
    }
  }
}


void Dialog::on_pbCommand_clicked()
{
  //the default directory is the user's home directory
  QString commandFolder = QDir::homePath();
  if (!ui->lCommand->text().isEmpty()) commandFolder = ui->lCommand->text().section('/', 0, -2);
  if (commandFolder.isEmpty()) commandFolder = QDir::homePath();

  QString fileName = QFileDialog::getOpenFileName(this,
        tr("Open command"), commandFolder, tr("All Files (*)"));
  if (!fileName.isEmpty()) {
    ui->lCommand->setText(fileName);
    ui->lCommand->setModified(true);
  }
}


void Dialog::on_pbWorkingDir_clicked()
{
  //the default directory is /
  QString workingDir = "/";
  if (ui->lWorkingDir->text().isEmpty()) workingDir = "/";
  else workingDir = ui->lWorkingDir->text();
  QFileDialog::Options options = QFileDialog::DontResolveSymlinks | QFileDialog::ShowDirsOnly;
  QString directory = QFileDialog::getExistingDirectory(this,
                                    tr("Working Directory"),
                                    workingDir,
                                    options);
  if (!directory.isEmpty()) {
    ui->lWorkingDir->setText(directory);
    ui->lWorkingDir->setModified(true);
  }
}

//we save the changes to the destination file
void Dialog::on_pbApply_clicked()
{
  QString from,to;
  QString desktopTypeVal="Application";
  if (DF.type == XDGDesktop::APP) { desktopTypeVal="Application"; }
  else if (DF.type == XDGDesktop::LINK) { desktopTypeVal="Link"; }
  else if (DF.type == XDGDesktop::DIR) { desktopTypeVal="Dir"; }
  textReplace(inMemoryFile, desktopTypeVal, desktopType, "Type");

  if (ui->lName->isModified()) { textReplace(inMemoryFile, DF.name, ui->lName->text(), "Name");}
  if (ui->lComment->isModified()) { textReplace(inMemoryFile, DF.comment, ui->lComment->text(), "Comment");}
  if (ui->lCommand->isModified()) { textReplace(inMemoryFile, DF.exec, ui->lCommand->text(),"Exec");}
  if (desktopType=="link") {
    //incase of "link" layout WorkingDir is corresponding to the URL
    if (ui->lWorkingDir->isModified()) { textReplace(inMemoryFile, DF.url, ui->lWorkingDir->text(),"URL");}
  } else {
    if (ui->lWorkingDir->isModified()) { textReplace(inMemoryFile, DF.path, ui->lWorkingDir->text(),"Path");}
  }
  if (ui->cbStartupNotification->isChecked() != DF.startupNotify) {
    if (DF.startupNotify) {from="true"; to="false";} else {from="false"; to="true";}
    textReplace(inMemoryFile, from, to,"StartupNotify");
  }
  if (ui->cbRunInTerminal->isChecked() != DF.useTerminal) {
    if (DF.useTerminal) {from="true"; to="false";} else {from="false"; to="true";}
    textReplace(inMemoryFile, from, to,"Terminal");
  }
  if (!iconFileName.isEmpty()) {
    from=DF.icon;
    to=iconFileName;
    textReplace(inMemoryFile, from, to,"Icon");
  }

  QFile file(desktopFileName);
  if (file.open(QFile::ReadWrite)) {
    file.seek(0);
    file.write(inMemoryFile.toUtf8());

    file.resize(file.pos());//remove possible trailing lines

    file.close();
  } else {
    //problem to write to the disk
    QMessageBox::critical(this, tr("Problem to write to disk"), tr("We have a problem to write the adapted desktop file to the disk. Can you re-try the modification after solving the issue with the disk ?"));
  }

  //hack required to update the icon on the desktop
  QTemporaryFile tempFile ;
  tempFile.setAutoRemove(false);
  tempFile.setFileTemplate("/tmp/lumina-XXXXXX");
  tempFile.open();
  tempFile.close();
  
  QString cmd = "mv";
  cmd = cmd + " " + desktopFileName + " " + tempFile.fileName();
  int ret = LUtils::runCmd(cmd);
  if (ret !=0 ) {
    qDebug() << "Problem to execute:" << cmd;
    QMessageBox::critical(this, tr("Problem to write to disk"), tr("We have a problem to execute the following command:") + cmd);
  }

  cmd = "mv";
  cmd = cmd + " " + tempFile.fileName() + " " + desktopFileName;
  ret = LUtils::runCmd(cmd);
  if (ret !=0 ) {
    qDebug() << "Problem to execute:" << cmd;
    QMessageBox::critical(this, tr("Problem to write to disk"), tr("We have a problem to execute the following command:") + cmd);
  }
}


void Dialog::on_pbIcon_clicked()
{
  //the default directory is local/share/icons
  QString iconFolder = LOS::AppPrefix()+"/share/icons";
  if (!iconFileName.isEmpty()) iconFolder = iconFileName.section('/', 0, -2);
  else if (!DF.icon.isEmpty()) iconFolder = DF.icon.section('/', 0, -2);
  if (iconFolder.isEmpty()) iconFolder = LOS::AppPrefix()+"/share/icons";

  QString fileName = QFileDialog::getOpenFileName(this,
        tr("Open command"), iconFolder, tr("Image Files (*.png *.jpg *.bmp)"));
  if (!fileName.isEmpty()) {
    ui->pbIcon->setIcon(QPixmap(fileName));
    iconFileName=fileName;
  }
}

void Dialog::on_lName_textChanged(QString text)
{
  if (text != DF.name && inMemoryFile.contains(QRegExp("\nName\\[\\S+\\]\\s*=",Qt::CaseInsensitive))) {
    QMessageBox msgBox;
    msgBox.setText(tr("By modifying this value, you will loose all translated versions"));
    msgBox.setInformativeText(tr("The field: Name is translated in several other languages. If you want to continue, you will loose all translated versions"));
    msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
    int answer = msgBox.exec();
    if (answer==QMessageBox::Ok) {
      //remove all translated versions. The lang cannot be null, but the value can be.
      inMemoryFile.replace(QRegExp("\nName\\[\\S+\\]\\s*=[^\n]*",Qt::CaseInsensitive), "");
    } else {
      ui->lName->setText(DF.name);
      }
  }
}

void Dialog::on_lComment_textChanged(QString text)
{
  if (text != DF.name && inMemoryFile.contains(QRegExp("\nComment\\[\\S+\\]\\s*=",Qt::CaseInsensitive))) {
    QMessageBox msgBox;
    msgBox.setText(tr("By modifying this value, you will loose all translated versions"));
    msgBox.setInformativeText(tr("The field: Comment is translated in several other languages. If you want to continue, you will loose all translated versions"));
    msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
    int answer = msgBox.exec();
    if (answer==QMessageBox::Ok) {
      //remove all translated versions. The lang cannot be null, but the value can be.
      inMemoryFile.replace(QRegExp("\nComment\\[\\S+\\]\\s*=[^\n]*",Qt::CaseInsensitive), "");
    } else {
      ui->lName->setText(DF.comment);
    }
  }
}

bgstack15