source: src-qt4/pc-softwaremanager/mainUI.cpp @ 24f4449

releng/10.0.1releng/10.0.2releng/10.0.3releng/10.1
Last change on this file since 24f4449 was 24f4449, checked in by Ken Moore <ken@…>, 9 months ago

Add a message/verificationn window when the maintainer button is clicked.

  • Property mode set to 100644
File size: 53.0 KB
Line 
1/***************************************************************************
2 *   Copyright (C) 2011 - iXsystems                                       *
3 *   kris@pcbsd.org  *
4 *   tim@pcbsd.org   *
5 *   ken@pcbsd.org   *
6 *                                                                         *
7 *   Permission is hereby granted, free of charge, to any person obtaining *
8 *   a copy of this software and associated documentation files (the       *
9 *   "Software"), to deal in the Software without restriction, including   *
10 *   without limitation the rights to use, copy, modify, merge, publish,   *
11 *   distribute, sublicense, and/or sell copies of the Software, and to    *
12 *   permit persons to whom the Software is furnished to do so, subject to *
13 *   the following conditions:                                             *
14 *                                                                         *
15 *   The above copyright notice and this permission notice shall be        *
16 *   included in all copies or substantial portions of the Software.       *
17 *                                                                         *
18 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
19 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
20 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
21 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR     *
22 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
23 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
24 *   OTHER DEALINGS IN THE SOFTWARE.                                       *
25 ***************************************************************************/
26#include "mainUI.h"
27#include "ui_mainUI.h"
28
29MainUI::MainUI(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainUI){
30  //initialization function
31  ui->setupUi(this); //load the Qt-Designer file
32  defaultIcon = ":/application.png";
33  statusLabel = new QLabel();
34  ui->statusbar->addWidget(statusLabel);
35}
36
37void MainUI::setWardenMode(QString dir, QString ip){
38  wardenDir = dir; wardenIP = ip;
39}
40
41void MainUI::ProgramInit()
42{ 
43   qDebug("Application starting...");
44   //Initial Checks
45     bool wardenMode = !wardenDir.isEmpty();
46     bool ok = Extras::checkUser(wardenMode);
47     if(!ok && wardenMode){  //Not root
48       QMessageBox::information( this, tr("Error!"), tr("The AppCafe must be run as root when in Warden compatibility mode") );
49       close();     
50     }else if( !ok && !wardenMode){
51       QMessageBox::information( this, tr("Error!"), tr("The AppCafe must be started with user permissions!")+"\n"+tr("The user must also be a part of the \"operator\" group") );
52       close();
53     }
54   
55   //Initialize the backend worker class
56   //qDebug() << "Initialize Backend";
57   PBI = new PBIBackend();
58     if(wardenMode){ PBI->setWardenMode(wardenDir, wardenIP); }
59     
60   //Initialize the Installed tab
61   //qDebug() << "Initialize Installed Tab";
62   initializeInstalledTab();
63   //Initialize the PBI Browser
64   //qDebug() << "Initialize Browser Tab";
65   initializeBrowserTab();
66   
67   //Now startup the backend
68   //qDebug() << "Startup Backend";
69     connect(PBI,SIGNAL(LocalPBIChanges()),this,SLOT(slotRefreshInstallTab()) );
70     connect(PBI,SIGNAL(PBIStatusChange(QString)),this,SLOT(slotPBIStatusUpdate(QString)) );
71     connect(PBI,SIGNAL(RepositoryInfoReady()),this,SLOT(slotEnableBrowser()) );
72     connect(PBI,SIGNAL(NoRepoAvailable()),this,SLOT(slotDisableBrowser()) );
73     connect(PBI,SIGNAL(SearchComplete(QStringList,QStringList)),this,SLOT(slotShowSearchResults(QStringList, QStringList)) );
74     connect(PBI,SIGNAL(SimilarFound(QStringList)),this,SLOT(slotShowSimilarApps(QStringList)) );
75     connect(PBI,SIGNAL(Error(QString,QString,QStringList)),this,SLOT(slotDisplayError(QString,QString,QStringList)) );
76     PBI->start();
77
78   //Make sure we start on the installed tab
79   ui->tabWidget->setCurrentWidget(ui->tab_installed);
80   ui->stackedWidget->setCurrentWidget(ui->page_install_list);
81}
82
83void MainUI::slotSingleInstance(){
84  this->raise();
85  this->showNormal();
86  this->activateWindow();
87}
88
89void MainUI::closeEvent(QCloseEvent *event){
90  bool safe = PBI->safeToQuit();
91  if(!safe){
92    //Verify that they want to continue
93    QMessageBox::StandardButton button = QMessageBox::warning(this, tr("AppCafe Processes Running"), tr("The AppCafe currently has actions pending. Do you want to cancel all running processes and quit anyway?"), QMessageBox::Yes | QMessageBox::Cancel,QMessageBox::Cancel);
94    if(button == QMessageBox::Yes){ //close down
95      PBI->cancelActions( PBI->installedList() ); //close down safely
96    }else{
97      event->ignore();
98      return;
99    }
100  }
101  this->close();
102}
103// ========================
104// ===== MENU OPTIONS =====
105// ========================
106void MainUI::on_actionImport_PBI_List_triggered(){
107  QString file = QFileDialog::getOpenFileName( this, tr("Import PBI File List"), QDir::homePath(), tr("PBI List (*.pbilist)"));
108  if(file.isEmpty()){ return; } //action cancelled
109  bool ok = PBI->importPbiListFromFile(file);
110  if(!ok){ qDebug() << QMessageBox::warning(this,tr("Import Error"),tr("There was an error importing the PBI list")+"\n"+tr("Please make sure that the file has not been corrupted and try again")); }
111}
112
113void MainUI::on_actionExport_PBI_List_triggered(){
114  QString file = QFileDialog::getSaveFileName( this, tr("Export PBI File List"), QDir::homePath()+"/exportfile.pbilist", tr("PBI List (*.pbilist)"));
115  if(file.isEmpty()){ return; } //action cancelled
116  bool ok = PBI->exportPbiListToFile(file);
117  if(!ok){ qDebug() << QMessageBox::warning(this,tr("Export Error"),tr("There was an error exporting the PBI list")+"\n"+tr("Please make sure that you have the proper directory permissions and try again")); }
118}
119
120void MainUI::on_actionQuit_triggered(){
121  this->close();
122}
123
124void MainUI::on_actionAppCafe_Settings_triggered(){
125  PBI->openConfigurationDialog();
126}
127
128void MainUI::on_actionInstall_From_File_triggered(){
129  QStringList files = QFileDialog::getOpenFileNames(this, tr("Install PBI"), QDir::homePath(), tr("PBI Application (*.pbi)") );
130  if(files.isEmpty()){ return; } //cancelled
131  //Verify that they want to install these applications
132  QStringList names;
133  for(int i=0; i<files.length(); i++){ names << files[i].section("/",-1); }
134  names.sort();
135  if( QMessageBox::Yes == QMessageBox::question(this, tr("Verify Installation"), tr("Are you ready to begin installing these PBI's?")+"\n"+tr("NOTE: You will need to manually add desktop/menu icons through the AppCafe afterwards.")+"\n\n"+names.join("\n"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) ){
136    //This might take a while, so don't allow the user to run this action again until it is done (thread safe though)
137    ui->actionInstall_From_File->setEnabled(false);
138    PBI->installPBIFromFile(files);
139    ui->actionInstall_From_File->setEnabled(true);
140  }
141}
142
143// =========================
144// ===== INSTALLED TAB =====
145// =========================
146void MainUI::initializeInstalledTab(){
147       
148  //Setup the action menu for installed applications
149  actionMenu = new QMenu();
150    actionMenu->addAction( QIcon(":icons/view-refresh.png"), tr("Update"), this, SLOT(slotActionUpdate()) );
151    actionMenu->addSeparator();
152    QMenu *dmenu = actionMenu->addMenu( QIcon(":icons/xdg_desktop.png"), tr("Desktop Icons"));
153      dmenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddDesktop()) );
154      dmenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemoveDesktop()) );
155    QMenu *mmenu = actionMenu->addMenu( QIcon(":icons/xdg_menu.png"), tr("Menu Icons"));
156      mmenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddMenu()) );
157      mmenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemoveMenu()) ); 
158      mmenu->addAction( QIcon(":icons/add-root.png"),tr("Add (All Users)"),this,SLOT(slotActionAddMenuAll()) );
159    QMenu *pmenu = actionMenu->addMenu( QIcon(":icons/xdg_paths.png"), tr("Path Links"));
160      pmenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddPath()) );
161      pmenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemovePath()) ); 
162      pmenu->addAction( QIcon(":icons/add-root.png"),tr("Add (All Users)"),this,SLOT(slotActionAddPathAll()) );
163    QMenu *fmenu = actionMenu->addMenu( QIcon(":icons/xdg_mime.png"), tr("File Associations"));
164      fmenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddMime()) );
165      fmenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemoveMime()) ); 
166      fmenu->addAction( QIcon(":icons/add-root.png"),tr("Add (All Users)"),this,SLOT(slotActionAddMimeAll()) );
167    actionMenu->addSeparator();
168    actionMenu->addAction( QIcon(":icons/remove.png"), tr("Uninstall"), this, SLOT(slotActionRemove()) );
169    actionMenu->addSeparator();
170    actionMenu->addAction( QIcon(":icons/dialog-cancel.png"), tr("Cancel Actions"), this, SLOT(slotActionCancel()) );
171  //Setup the shortcuts menu for installed applications
172  shortcutMenu = new QMenu(this);
173    sDeskMenu = shortcutMenu->addMenu( QIcon(":icons/xdg_desktop.png"), tr("Desktop Icons"));
174      sDeskMenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddDesktop()) );
175      sDeskMenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemoveDesktop()) );
176    sMenuMenu = shortcutMenu->addMenu( QIcon(":icons/xdg_menu.png"), tr("Menu Icons"));
177      sMenuMenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddMenu()) );
178      sMenuMenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemoveMenu()) ); 
179      sMenuMenu->addAction( QIcon(":icons/add-root.png"),tr("Add (All Users)"),this,SLOT(slotActionAddMenuAll()) );
180    QMenu *spmenu = shortcutMenu->addMenu( QIcon(":icons/xdg_paths.png"), tr("Path Links"));
181      spmenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddPath()) );
182      spmenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemovePath()) ); 
183      spmenu->addAction( QIcon(":icons/add-root.png"),tr("Add (All Users)"),this,SLOT(slotActionAddPathAll()) );
184    sMimeMenu = shortcutMenu->addMenu( QIcon(":icons/xdg_mime.png"), tr("File Associations"));
185      sMimeMenu ->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddMime()) );
186      sMimeMenu ->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemoveMime()) ); 
187      sMimeMenu ->addAction( QIcon(":icons/add-root.png"),tr("Add (All Users)"),this,SLOT(slotActionAddMimeAll()) );
188  ui->tool_install_shortcuts->setMenu(shortcutMenu);
189  //Setup the binary menu for installed applications
190  appBinMenu = new QMenu();
191  ui->tool_install_startApp->setMenu(appBinMenu);
192    connect(appBinMenu, SIGNAL(triggered(QAction*)), this, SLOT(slotStartApp(QAction*)) );
193  //Initialize the context menu
194  contextActionMenu = new QMenu(this);
195    connect(contextActionMenu, SIGNAL(aboutToHide()), this, SLOT(contextMenuFinished()) );
196  //Now setup the action button
197  ui->tool_install_performaction->setMenu(actionMenu);
198  ui->tool_install_performaction->setPopupMode(QToolButton::InstantPopup);
199  //Now setup any defaults for the installed tab
200  ui->tree_install_apps->setIconSize(QSize(22,22));
201  ui->tree_install_apps->setContextMenuPolicy(Qt::CustomContextMenu);
202  connect(ui->tree_install_apps, SIGNAL(itemClicked(QTreeWidgetItem*, int)), this, SLOT(slotCheckSelectedItems()) );
203  connect(ui->tree_install_apps, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT( slotInstalledAppRightClicked(const QPoint &)) );
204  slotRefreshInstallTab();
205}
206
207void MainUI::formatInstalledItemDisplay(QTreeWidgetItem *item){
208  //simplification function for filling the tree widget item with the appropriate information about the PBI
209  QString ID = item->whatsThis(0);
210  if(item->text(0).isEmpty()){  //new entry - get everything
211    QStringList vals; vals <<"name"<<"version"<<"status"; //same as column order
212    if(!vals.isEmpty()){
213      vals = PBI->PBIInfo(ID, vals);
214      for(int i=0; i<vals.length(); i++){ item->setText(i,vals[i]); }
215      QString icon = PBI->PBIInfo(ID, QStringList("icon")).join("");
216        //Load a default icon if none found
217      if(icon.isEmpty()){ icon = defaultIcon; }
218      item->setIcon(0,QIcon(icon) );
219      item->setCheckState(0,Qt::Unchecked);
220    }
221  }else{ // Just update the necesary info
222    QStringList vals; vals << "version" << "status";
223    vals = PBI->PBIInfo(ID,vals);
224    if(!vals.isEmpty()){ item->setText(1,vals[0]); item->setText(2,vals[1]); }
225  }
226}
227
228QStringList MainUI::getCheckedItems(){
229  //Return the pbiID's of all the active items
230  QStringList output;
231  //See if we are on the single-app details page or custom context menu- then get the current app only
232  if(ui->stackedWidget->currentWidget() == ui->page_install_details){
233      output << cDetails; 
234         
235  //Check for whether this is the context menu on the main widget
236  }else if(!cDetails.isEmpty()){
237     output << cDetails;
238     cDetails.clear();
239         
240  //If on the main Installed page, look for checked items only
241  }else{
242    for(int i=0; i<ui->tree_install_apps->topLevelItemCount(); i++){
243      if(ui->tree_install_apps->topLevelItem(i)->checkState(0) == Qt::Checked){
244        output << ui->tree_install_apps->topLevelItem(i)->whatsThis(0);
245      }
246    }
247  }
248  //qDebug() << "Checked Items:" << output;
249  return output;       
250}
251
252// === SLOTS ===
253void MainUI::slotRefreshInstallTab(){
254  //Update the list of installed PBI's w/o clearing the list (loses selections)
255   //Get the list we need (in order)
256  QStringList installList = PBI->installedList();
257  installList.sort(); //sort in ascending order
258   //Get the list we have now
259  QStringList cList;
260  for(int i=0; i<ui->tree_install_apps->topLevelItemCount(); i++){
261    cList << ui->tree_install_apps->topLevelItem(i)->whatsThis(0);
262  }
263  //Quick finish if no items installed
264  if(installList.isEmpty()){
265    ui->tree_install_apps->clear();
266  }else{
267    //Now make adjustments as necessary
268    for(int i=0; i<installList.length(); i++){
269      //Detemine what action should be done with this item location
270      int todo = 0; //0=insert new item, 1=update current item, 2=remove current item
271      if(i < cList.length()){
272        if(installList[i] == cList[i]){ todo=1; }
273        else if( installList.contains(cList[i]) && !cList.contains(installList[i]) ){ todo=0; } //new item to be inserted here
274        else if( !installList.contains(cList[i]) ){ todo=2; } //current item in this location should be removed
275      }
276      //Now perform the action on this location
277      if(todo==0){ 
278        //insert new item
279        QTreeWidgetItem *item = new QTreeWidgetItem; //create the item
280        item->setWhatsThis(0,installList[i]);
281        //Now format the display
282        formatInstalledItemDisplay(item);
283        if(item->text(0).isEmpty()){
284          //Do not put empty items into the display
285          delete item;
286        }else{
287          //Now insert this item onto the list
288          ui->tree_install_apps->insertTopLevelItem(i,item);
289          cList.insert(i,installList[i]); //reflect this inclusion into the current list
290        }
291      }else if(todo==1){
292        //Update current item
293        formatInstalledItemDisplay( ui->tree_install_apps->topLevelItem(i) );
294      }else{
295        //Remove current item
296        ui->tree_install_apps->takeTopLevelItem(i);
297        cList.removeAt(i); //reflect the change to the current list
298        i--; //Re-check the item that should be in this location
299      }
300    }
301    //Now makesure that there are no extra items at the end
302    int il = installList.length();
303    while(il < cList.length()){
304      ui->tree_install_apps->takeTopLevelItem(il);
305      cList.removeAt(il); //reflect the change to the current list
306    }
307  } //end of empty list check
308 
309  //Make sure that there is an item selected
310  if(ui->tree_install_apps->topLevelItemCount() > 0 ){
311    if( ui->tree_install_apps->selectedItems().isEmpty() ){
312      ui->tree_install_apps->setCurrentItem( ui->tree_install_apps->topLevelItem(0) );
313    }
314    //Now re-size the columns to the minimum required width
315    for(int i=0; i<3; i++){
316      ui->tree_install_apps->resizeColumnToContents(i);
317    } 
318  }
319  //If the installed app page is visible, be sure to update it too
320  if( ui->stackedWidget->currentWidget() == ui->page_install_details){
321    updateInstallDetails(cDetails);
322  }
323  slotUpdateSelectedPBI();; //Update the info boxes
324  slotDisplayStats();
325  slotCheckSelectedItems();
326  //If the browser app page is currently visible for this app
327  if( (ui->stacked_browser->currentWidget() == ui->page_app) && ui->page_app->isVisible() ){
328    slotGoToApp(cApp);
329  }
330}
331
332void MainUI::slotCheckSelectedItems(){
333  bool chkd = false;
334  for(int i=0; i<ui->tree_install_apps->topLevelItemCount(); i++){
335    if(ui->tree_install_apps->topLevelItem(i)->checkState(0) == Qt::Checked){
336      chkd = true; break;
337    }
338  }
339  ui->tool_install_performaction->setEnabled(chkd);
340  if(ui->stackedWidget->currentWidget() != ui->page_install_details){
341    cDetails.clear(); //Make sure this is cleared if not on the details page
342  }
343}
344
345void MainUI::slotPBIStatusUpdate(QString pbiID){
346  //This will do a full update of a particlar PBI entry
347  //    and just update/check the icons for all the other PBI's
348  for(int i=0; i<ui->tree_install_apps->topLevelItemCount(); i++){
349    QString itemID = ui->tree_install_apps->topLevelItem(i)->whatsThis(0);
350    if(itemID == pbiID){
351      QString stat = PBI->PBIInfo(pbiID,QStringList()<<"status").join("");
352      ui->tree_install_apps->topLevelItem(i)->setText(2,stat);
353      // See if we need to update the download progress bar
354      QString appID = ui->tree_install_apps->currentItem()->whatsThis(0);
355      if ( appID == pbiID ) {
356        slotUpdateSelectedPBI();
357        //If the details page is currently visible, update it too
358        if(ui->stackedWidget->currentWidget() == ui->page_install_details){
359          updateInstallDetails(appID);
360        }
361      }
362    }else{
363      //Just check/update the icon if necessary
364      if(ui->tree_install_apps->topLevelItem(i)->icon(0).isNull()){
365         ui->tree_install_apps->topLevelItem(i)->setIcon(0, QIcon( PBI->PBIInfo(itemID, QStringList() << "icon").join("") ));
366      }
367    }
368  }
369  //If the browser app page is current for this app
370  QString metaID = PBI->pbiToAppID(pbiID);
371  if( (ui->stacked_browser->currentWidget() == ui->page_app) && (cApp == metaID) && ui->page_app->isVisible() ){
372    slotUpdateAppDownloadButton();
373  }
374}
375
376void MainUI::on_tool_install_details_clicked(){
377  //Get the current item
378  QString appID;
379  if(ui->tree_install_apps->topLevelItemCount() > 0){
380    appID = ui->tree_install_apps->currentItem()->whatsThis(0);
381  }
382  if(appID.isEmpty()){return;}
383  //Update the info on the details page
384  updateInstallDetails(appID);
385  //Now show the page
386  ui->stackedWidget->setCurrentWidget(ui->page_install_details);
387}
388
389void MainUI::on_tool_install_back_clicked(){
390  //List page should always be current based upon backend
391  ui->stackedWidget->setCurrentWidget(ui->page_install_list);
392}
393
394void MainUI::on_tool_install_gotobrowserpage_clicked(){
395  //When you want to open up the browser page for an application
396  QString appID = Extras::nameToID( ui->tree_install_apps->currentItem()->text(0) );
397  slotGoToApp(appID);
398}
399
400void MainUI::on_tool_install_toggleall_clicked(){
401  //Determine if the current item is checked or unchecked
402  bool checkall = (ui->tree_install_apps->currentItem()->checkState(0) == Qt::Unchecked);
403  for(int i=0; i<ui->tree_install_apps->topLevelItemCount(); i++){
404    if(checkall){ ui->tree_install_apps->topLevelItem(i)->setCheckState(0,Qt::Checked); }
405    else{ui->tree_install_apps->topLevelItem(i)->setCheckState(0,Qt::Unchecked); }
406  }
407  slotCheckSelectedItems();
408}
409
410void MainUI::on_tree_install_apps_itemSelectionChanged(){
411  //When an installed PBI is clicked on
412  slotUpdateSelectedPBI();
413  if(ui->stackedWidget->currentWidget() != ui->page_install_list){
414    ui->stackedWidget->setCurrentWidget(ui->page_install_list);
415  }
416  slotCheckSelectedItems();
417}
418
419void MainUI::on_tree_install_apps_itemDoubleClicked(QTreeWidgetItem *item){
420 //Make sure it is a valid/installed application
421 QString appID = item->whatsThis(0);
422  appID = PBI->isInstalled(appID);
423  if(appID.isEmpty()){ return; } //invalid item
424  //Update the info on the details page
425  updateInstallDetails(appID);
426  //Now show the page
427  ui->stackedWidget->setCurrentWidget(ui->page_install_details);
428}
429
430void MainUI::on_check_install_autoupdate_clicked(){
431  //Get the current item
432  QString appID;
433  if(ui->tree_install_apps->topLevelItemCount() > 0){
434    appID = ui->tree_install_apps->currentItem()->whatsThis(0);
435  }
436  if(appID.isEmpty()){return;}
437  //Now determine the current state of the checkbox
438  bool enabled = ui->check_install_autoupdate->isChecked();
439  //Now have the backend make the change
440  PBI->enableAutoUpdate(appID, enabled);
441  //Now ask if the user also wants to start updating it now
442  if(enabled && !PBI->upgradeAvailable(appID).isEmpty()){
443    if( QMessageBox::Yes == QMessageBox::question(this, tr("Start Update?"), tr("Do you wish to start updating this application right now?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) ){
444      PBI->upgradePBI(QStringList() << appID);
445    }
446  }
447  //Now force the info on the page to be updated in a moment (need time for database to update)
448  //QTimer::singleShot(500, this, SLOT(on_tool_install_details_clicked()) );
449}
450
451void MainUI::on_tool_install_update_clicked(){
452  //Get the current item
453  QString appID;
454  if(ui->tree_install_apps->topLevelItemCount() > 0){
455    appID = ui->tree_install_apps->currentItem()->whatsThis(0);
456  }
457  if(appID.isEmpty()){return;}
458  PBI->upgradePBI(QStringList() << appID);
459}
460
461void MainUI::on_tool_install_remove_clicked(){
462  //Get the current item
463  QString appID;
464  if(ui->tree_install_apps->topLevelItemCount() > 0){
465    appID = ui->tree_install_apps->currentItem()->whatsThis(0);
466  }
467  if(appID.isEmpty()){return;}
468  //Verify removal
469  if( QMessageBox::Yes == QMessageBox::question(this,tr("Verify PBI Removal"), tr("Are you sure you wish to remove this application?")+"\n\n"+appID,QMessageBox::Yes | QMessageBox::Cancel,QMessageBox::Cancel) ){
470    PBI->removePBI(QStringList() << appID);
471  }
472}
473
474void MainUI::on_tool_install_cancel_clicked(){
475  //Get the current item
476  QString appID;
477  if(ui->tree_install_apps->topLevelItemCount() > 0){
478    appID = ui->tree_install_apps->currentItem()->whatsThis(0);
479  }
480  if(appID.isEmpty()){return;}
481  PBI->cancelActions(QStringList() << appID);
482 
483}
484
485void MainUI::on_tool_install_maintainer_clicked(){
486  //Get the current item
487  QString appID;
488  if(ui->tree_install_apps->topLevelItemCount() > 0){
489    appID = ui->tree_install_apps->currentItem()->whatsThis(0);
490  }
491  if(appID.isEmpty()){return;}
492  //Get the maintainer email
493  QString email = PBI->PBIInfo(appID, QStringList() << "maintainer").join("");
494  if(email.isEmpty()){ return; }
495  //Verify that the user wants to launch their email client
496  if(QMessageBox::Yes != QMessageBox::question(this, tr("Launch Email Client?"), tr("Do you want to try launching your default email client? \n You must have this setup within your current desktop environment for this to work properly. If not, you can send an email to the address below manually.")+"\n\n"+email, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) ){
497    return;
498  }
499  qDebug() << "Launching email to:" << email;
500  //Get the command from the action
501  QString cmd = "mailto:"+email;
502  QStringList info = PBI->PBIInfo(appID, QStringList() << "name" << "date" << "version" << "arch" << "fbsdversion" );
503  //Add a sample subject
504  cmd.append("?subject="+info[0]+" port question");
505  //Add the info to the body of the email
506  cmd.append("&body=");
507  cmd.append("-----------\nPBI Information:\nName: "+info[0] + "\nDate Created: "+info[1] +"\nVersion: "+info[2] +"\nArchitecture: "+info[3] +"\nFreeBSD Version: "+info[4] );
508  //Startup the command externally
509  QProcess::execute("xdg-open \""+cmd+"\"");
510}
511
512void MainUI::slotInstalledAppRightClicked(const QPoint &pt){
513  //Get the item under the mouse click
514  QTreeWidgetItem *it = ui->tree_install_apps->itemAt(pt);
515  if(it==0){ return; } // no item selected
516  QString pbiID = it->whatsThis(0);
517  qDebug() << "Get context menu for:" << pbiID;
518  //Now Update the context menu appropriately
519  QStringList info = PBI->PBIInfo(pbiID, QStringList() << "hasdesktopicons" << "hasmenuicons" << "hasmimetypes");
520  if(info.isEmpty()){ return; } //invalid application
521  bool pending = PBI->isWorking(pbiID);
522  contextActionMenu->clear();
523  if(!PBI->upgradeAvailable(pbiID).isEmpty() &&  !pending){
524    //Upgrade is only available if actions not pending
525    contextActionMenu->addAction( QIcon(":icons/view-refresh.png"), tr("Update"), this, SLOT(slotActionUpdate()) );
526    contextActionMenu->addSeparator();
527  }
528  if(info[0]=="true"){
529    QMenu *dmenu = contextActionMenu->addMenu( QIcon(":icons/xdg_desktop.png"), tr("Desktop Icons"));
530      dmenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddDesktop()) );
531      dmenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemoveDesktop()) );
532  }
533  if(info[1]=="true"){
534    QMenu *mmenu = contextActionMenu->addMenu( QIcon(":icons/xdg_menu.png"), tr("Menu Icons"));
535      mmenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddMenu()) );
536      mmenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemoveMenu()) ); 
537      mmenu->addAction( QIcon(":icons/add-root.png"),tr("Add (All Users)"),this,SLOT(slotActionAddMenuAll()) );
538  }
539  //Paths are always available
540    QMenu *pmenu = contextActionMenu->addMenu( QIcon(":icons/xdg_paths.png"), tr("Path Links"));
541      pmenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddPath()) );
542      pmenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemovePath()) ); 
543      pmenu->addAction( QIcon(":icons/add-root.png"),tr("Add (All Users)"),this,SLOT(slotActionAddPathAll()) );
544  if(info[2]=="true"){
545    QMenu *fmenu = contextActionMenu->addMenu( QIcon(":icons/xdg_mime.png"), tr("File Associations"));
546      fmenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddMime()) );
547      fmenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemoveMime()) ); 
548      fmenu->addAction( QIcon(":icons/add-root.png"),tr("Add (All Users)"),this,SLOT(slotActionAddMimeAll()) );
549  }
550  if(!pending){
551    //Remove option is only available if not currently pending actions
552    contextActionMenu->addSeparator();
553    contextActionMenu->addAction( QIcon(":icons/remove.png"), tr("Uninstall"), this, SLOT(slotActionRemove()) );
554  }
555  if(pending){
556    //Cancel option is only available if actions are currently pending   
557    contextActionMenu->addSeparator();
558    contextActionMenu->addAction( QIcon(":icons/dialog-cancel.png"), tr("Cancel Actions"), this, SLOT(slotActionCancel()) );
559  }
560  //Now show the menu
561  cDetails = pbiID; //save this so we know which app is currently being modified
562  contextActionMenu->popup(ui->tree_install_apps->mapToGlobal(pt));
563}
564
565void MainUI::contextMenuFinished(){
566  QTimer::singleShot(500, this, SLOT(slotCheckSelectedItems()) );       
567}
568
569// === SELECTED PBI ACTIONS ===
570void MainUI::slotActionAddDesktop(){
571  QStringList items = getCheckedItems();
572  PBI->addDesktopIcons(items,FALSE); //only for current user
573}
574
575void MainUI::slotActionRemoveDesktop(){
576  QStringList items = getCheckedItems();
577  PBI->rmDesktopIcons(items,FALSE);  //Only for current user
578}
579
580void MainUI::slotActionAddPath(){
581  QStringList items = getCheckedItems();
582  PBI->addPaths(items,FALSE);  //Only for current user 
583}
584
585void MainUI::slotActionRemovePath(){
586  QStringList items = getCheckedItems();
587  PBI->rmPaths(items,FALSE);  //Only for current user
588}
589
590void MainUI::slotActionAddPathAll(){
591  QStringList items = getCheckedItems();
592  PBI->addPaths(items,TRUE);  //For all users (root permissions required)
593}
594
595void MainUI::slotActionAddMenu(){
596  QStringList items = getCheckedItems();
597  PBI->addMenuIcons(items,FALSE);  //Only for current user
598}
599
600void MainUI::slotActionRemoveMenu(){
601  QStringList items = getCheckedItems();
602  PBI->rmMenuIcons(items,FALSE);  //Only for current user       
603}
604
605void MainUI::slotActionAddMenuAll(){
606  QStringList items = getCheckedItems();
607  PBI->addMenuIcons(items,TRUE);  //For all users (root permissions required)   
608}
609
610void MainUI::slotActionAddMime(){
611  QStringList items = getCheckedItems();
612  PBI->addMimeTypes(items,FALSE);  //Only for current user     
613}
614
615void MainUI::slotActionRemoveMime(){
616  QStringList items = getCheckedItems();
617  PBI->rmMimeTypes(items,FALSE);  //Only for current user       
618}
619
620void MainUI::slotActionAddMimeAll(){
621    QStringList items = getCheckedItems();
622  PBI->addMimeTypes(items,TRUE);  //For all users (root permissions required)   
623}
624
625void MainUI::slotActionUpdate(){
626  QStringList checkedID = getCheckedItems();
627  if(!checkedID.isEmpty()){
628    PBI->upgradePBI(checkedID); 
629  }
630}
631
632void MainUI::slotActionRemove(){
633  QStringList checkedID = getCheckedItems();
634  if(!checkedID.isEmpty()){
635    //Verify that the user really wants to remove these apps
636    if( QMessageBox::Yes == QMessageBox::question(this,tr("Verify PBI Removal"), tr("Are you sure you wish to remove these applications?")+"\n\n"+checkedID.join("\n"),QMessageBox::Yes | QMessageBox::Cancel,QMessageBox::Cancel) ){
637      PBI->removePBI(checkedID);
638    }
639  }
640}
641
642void MainUI::slotActionCancel(){
643  QStringList checkedID = getCheckedItems();
644  if(!checkedID.isEmpty()){
645    PBI->cancelActions(checkedID); 
646  }
647}
648
649void MainUI::slotStartApp(QAction* act){
650  qDebug() << "Starting external application:" << act->text();
651  //Get the command from the action
652  QString desktopfile = act->whatsThis();
653  QString cmd = "xdg-open "+desktopfile;
654  //Startup the command externally
655  QProcess::execute(cmd);
656}
657
658void MainUI::slotUpdateSelectedPBI(){
659  //Get the currently selected app
660  QString appID;
661  if(ui->tree_install_apps->topLevelItemCount() > 0){
662    appID = ui->tree_install_apps->currentItem()->whatsThis(0);
663  }
664  //See if this one is actually installed
665  appID = PBI->isInstalled(appID);
666  if(appID.isEmpty()){
667    ui->tool_install_details->setEnabled(false);
668  }else{
669    ui->tool_install_details->setEnabled(true);   
670  }
671   
672}
673
674void MainUI::updateInstallDetails(QString appID){
675  //Get the information to update the details page
676  //Get the PBI info for that item
677    cDetails = appID; //save for later
678    QStringList vals; 
679    vals << "name" << "icon" << "author" << "website" << "version" << "license" << "description" << "maintainer" << "date" << "arch";
680    QStringList bools;
681    bools << "autoupdate" << "hasdesktopicons" << "hasmenuicons" << "requiresroot" << "hasmimetypes";
682    vals = PBI->PBIInfo(appID,vals);
683    bools = PBI->PBIInfo(appID,bools);
684    //Make sure the info lists are not empty
685    if(vals.isEmpty() || bools.isEmpty()){
686      ui->tool_install_details->setEnabled(false);
687      return; 
688    }
689    //Load a default icon if none found
690    if(vals[1].isEmpty()){ vals[1] = defaultIcon; }
691    //Now set the info on the UI
692    bool desktopSC = (bools[1] == "true"); // XDG desktop entries available
693    bool menuSC= (bools[2] == "true");   // XDG menu entries available
694    bool autoupdate = (bools[0] == "true"); //auto-update enabled
695    bool rootonly = (bools[3] == "true"); //requires root
696    bool mimeSC = (bools[4] == "true"); //XDG mime entries available
697    //Create the shortcuts string
698    sDeskMenu->setEnabled(desktopSC);
699    sMenuMenu->setEnabled(menuSC);
700    sMimeMenu->setEnabled(mimeSC);
701    //Now display that info on the UI
702    ui->label_install_app->setText(vals[0]);
703    ui->label_install_icon->setPixmap( QPixmap(vals[1]).scaled(64,64, Qt::KeepAspectRatio, Qt::SmoothTransformation) );
704    if(vals[3].isEmpty()){ 
705      ui->label_install_author->setText(vals[2]); 
706      ui->label_install_author->setToolTip("");
707    }else{ 
708      ui->label_install_author->setText("<a href="+vals[3]+">"+vals[2]+"</a>"); 
709      ui->label_install_author->setToolTip(vals[3]); //show website URL as tooltip
710    }
711    ui->label_install_version->setText(vals[4]);
712    ui->label_install_license->setText(vals[5]);
713    ui->text_install_description->setPlainText(vals[6]);
714    ui->tool_install_maintainer->setVisible( vals[7].contains("@") );
715    ui->label_install_date->setText(vals[8]);
716    ui->label_install_arch->setText(vals[9]);
717    //ui->label_install_shortcuts->setText(shortcuts);
718    ui->check_install_autoupdate->setChecked(autoupdate);
719 
720    //Adjust the quick action buttons as necessary
721    if( PBI->isWorking(appID) ){
722      //Actions pending/working only show cancel button
723      ui->tool_install_cancel->setVisible(TRUE);
724      ui->tool_install_remove->setVisible(FALSE);
725      ui->tool_install_update->setVisible(FALSE);
726      ui->tool_install_startApp->setVisible(FALSE);
727    }else{
728      //Nothing pending
729      ui->tool_install_cancel->setVisible(FALSE);
730      if( !PBI->isInstalled(appID).isEmpty() ){ 
731        //Remove Button
732        ui->tool_install_remove->setVisible(TRUE);
733        if(rootonly){ ui->tool_install_remove->setIcon(QIcon(":icons/remove-root.png")); }
734        else{ ui->tool_install_remove->setIcon(QIcon(":icons/remove.png")); }
735        //Update
736        if(PBI->upgradeAvailable(appID).isEmpty()){ ui->tool_install_update->setVisible(FALSE); }
737        else{
738          ui->tool_install_update->setVisible(TRUE); 
739          if(rootonly){ ui->tool_install_update->setIcon(QIcon(":icons/app_upgrade_small-root.png")); }
740          else{ ui->tool_install_update->setIcon(QIcon(":icons/app_upgrade_small.png")); }
741        }
742        //Start Application binaries
743        QStringList bins = PBI->pbiBinList(appID);
744        appBinMenu->clear();
745        for(int i=0; i<bins.length(); i++){
746          QAction *act = new QAction(this);
747            act->setText(bins[i].section("::::",0,0)); //set name
748            act->setWhatsThis(bins[i].section("::::",1,10)); //set command string
749          appBinMenu->addAction(act);
750        }
751        if(appBinMenu->isEmpty()){ ui->tool_install_startApp->setVisible(FALSE); }
752        else{ ui->tool_install_startApp->setVisible(TRUE); }
753      }else{ 
754        ui->tool_install_remove->setVisible(FALSE); 
755        ui->tool_install_update->setVisible(FALSE); 
756        ui->tool_install_startApp->setVisible(FALSE);
757      }   
758    }
759
760  //Update the current status indicators
761  QString stat = PBI->currentAppStatus(appID,true); //get the raw status
762  QString statF = PBI->currentAppStatus(appID, false); //get the non-raw status
763  if( stat.isEmpty() && statF.isEmpty() ){
764    //Not currently running - hide the display indicators
765    ui->group_install_appStat->setVisible(false);
766  }else{
767    //Currently installing/removing/updating - show last message from process
768    if(!statF.isEmpty()){ ui->label_install_status->setText(statF); }
769    else{ ui->label_install_status->setText(stat); }
770    ui->group_install_appStat->setVisible(TRUE);
771  }
772}
773
774// ==========================
775// ====== BROWSER TAB =======
776// ==========================
777void MainUI::initializeBrowserTab(){
778  ui->tab_browse->setEnabled(FALSE);
779  //Always make sure that the browser starts off on the "home" page
780  ui->stacked_browser->setCurrentWidget(ui->page_home);
781  //With invisible shortcut buttons
782  ui->tool_browse_cat->setVisible(FALSE);
783  ui->tool_browse_app->setVisible(FALSE);
784  //Clear any items left over from the designer form
785  clearScrollArea(ui->scroll_br_home_newapps);
786  clearScrollArea(ui->scroll_br_home_rec);
787  //Search functionality
788  searchTimer = new QTimer();
789    searchTimer->setSingleShot(TRUE);
790    searchTimer->setInterval(300); // 0.3 sec wait before a search
791    connect(searchTimer,SIGNAL(timeout()),this,SLOT(slotGoToSearch()) );
792  connect(ui->tool_browse_search,SIGNAL(clicked()),this,SLOT(slotGoToSearch()) );
793  connect(ui->line_browse_searchbar,SIGNAL(returnPressed()),this,SLOT(slotGoToSearch()) );
794  connect(ui->tool_browse_gotocat, SIGNAL(clicked()), this, SLOT(slotGoToCatBrowser()) );
795}
796
797// === SLOTS ===
798void MainUI::slotDisableBrowser(bool shownotification){
799  if(shownotification){ qDebug() << "No Repo Available: De-activating the Browser"; }
800  ui->tabWidget->setCurrentWidget(ui->tab_installed);
801  ui->tab_browse->setEnabled(FALSE);
802  slotDisplayStats();
803}
804
805void MainUI::slotEnableBrowser(){
806  qDebug() << "Repo Ready: - generating browser home page";
807  //Now create the browser home page
808  slotUpdateBrowserHome();
809  //And allow the user to go there
810  ui->tab_browse->setEnabled(TRUE);
811  slotDisplayStats();
812}
813
814void MainUI::slotUpdateBrowserHome(){
815  //Load the Recommendations
816  clearScrollArea(ui->scroll_br_home_rec);
817  QVBoxLayout *reclayout = new QVBoxLayout;
818  QStringList recList = PBI->getRecommendations();
819  QStringList info; info << "name" << "shortdescription" << "icon" << "type";
820  for(int i=0; i<recList.length(); i++){
821    QStringList data = PBI->AppInfo(recList[i],info);
822    if(!data.isEmpty()){
823      LargeItemWidget *item = new LargeItemWidget(recList[i],data[0],data[1],data[2]);
824      //Set the type icon
825      item->setType(data[3].toLower());
826      connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToApp(QString)) );
827      reclayout->addWidget(item);
828    }
829  }
830  reclayout->addStretch(); //add a spacer to the end
831  ui->scroll_br_home_rec->widget()->setLayout(reclayout);
832  //Load the newest applications
833  clearScrollArea(ui->scroll_br_home_newapps);
834  QHBoxLayout *newapplayout = new QHBoxLayout;
835  QStringList newapps = PBI->getRecentApps();
836  for(int i=0; i<newapps.length(); i++){
837    QStringList appdata = PBI->AppInfo(newapps[i],QStringList() << "name" << "icon" << "latestversion");
838    if(!appdata.isEmpty()){
839      SmallItemWidget *item = new SmallItemWidget(newapps[i],appdata[0],appdata[1],appdata[2]);
840      connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToApp(QString)) );
841      newapplayout->addWidget(item);
842    }
843  }
844  newapplayout->addStretch(); //add a spacer to the end
845  newapplayout->setContentsMargins(0,0,0,0);
846  newapplayout->setSpacing(0);
847  ui->scroll_br_home_newapps->widget()->setLayout(newapplayout);
848  //Make sure that the newapps scrollarea is the proper fit vertically (no vertical scrolling)
849  ui->scroll_br_home_newapps->setMinimumHeight(ui->scroll_br_home_newapps->widget()->minimumSizeHint().height());
850 
851  //Make sure the new apps area is invisible if no items available
852  if(newapps.isEmpty()){ ui->group_br_home_newapps->setVisible(FALSE); }
853  else{ ui->group_br_home_newapps->setVisible(TRUE); }
854  //make sure the home page is visible in the browser (slotGoToHome without changing tabs)
855  ui->stacked_browser->setCurrentWidget(ui->page_home); 
856  //Make sure the shortcut buttons are disabled
857  ui->tool_browse_cat->setVisible(FALSE);
858  ui->tool_browse_app->setVisible(FALSE); 
859 
860  //Now update the category browser page (since it only needs to be done once like the home menu)
861  //Load the Categories
862  QStringList cats = PBI->browserCategories();
863    cats.sort();
864    clearScrollArea(ui->scroll_br_cats);
865    QVBoxLayout *catlayout = new QVBoxLayout;
866    info.clear(); info << "name" << "description" << "icon";
867    for(int i=0; i<cats.length(); i++){
868      QStringList data = PBI->CatInfo(cats[i],info);
869      if(!data.isEmpty()){
870        LargeItemWidget *item = new LargeItemWidget(cats[i],data[0],data[1],data[2]);
871        connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToCategory(QString)) );
872        catlayout->addWidget(item);
873      }
874    }
875    catlayout->addStretch(); //add a spacer to the end
876    ui->scroll_br_cats->widget()->setLayout(catlayout);
877}
878
879void MainUI::slotGoToHome(){
880  ui->tabWidget->setCurrentWidget(ui->tab_browse);
881  ui->stacked_browser->setCurrentWidget(ui->page_home); 
882  //Make sure the shortcut buttons are disabled
883  ui->tool_browse_cat->setVisible(false);
884  ui->tool_browse_app->setVisible(false);
885  ui->tool_browse_gotocat->setVisible(true);
886}
887
888void MainUI::slotGoToCatBrowser(){
889  ui->tabWidget->setCurrentWidget(ui->tab_browse);
890  ui->stacked_browser->setCurrentWidget(ui->page_browsecats);
891  //Make sure the shortcut buttons are diabled
892  ui->tool_browse_cat->setVisible(false);
893  ui->tool_browse_app->setVisible(false);
894  ui->tool_browse_gotocat->setVisible(false);
895}
896
897void MainUI::slotGoToCategory(QString cat){
898  qDebug() << "Show Category:" << cat;
899  //Get the apps in this category
900  QStringList apps = PBI->browserApps(cat);
901  if(apps.isEmpty()){
902    qDebug() << "Invalid category:" << cat;
903    return;
904  }
905  clearScrollArea(ui->scroll_br_cat_apps);
906  apps.sort();
907  QVBoxLayout *applayout = new QVBoxLayout;
908  QStringList info; info << "name" << "shortdescription" << "icon" << "type";
909  for(int i=0; i<apps.length(); i++){
910    QStringList data = PBI->AppInfo(apps[i],info);
911    if(!data.isEmpty()){
912      //Trim the description if necessary
913      if(data[1].size()>100){ data[1] = data[1].left(100).append("..."); }
914      LargeItemWidget *item = new LargeItemWidget(apps[i],data[0],data[1],data[2]);
915      item->setType(data[3].toLower());
916      connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToApp(QString)) );
917      applayout->addWidget(item); 
918    }
919  }
920  applayout->addStretch();
921  ui->scroll_br_cat_apps->widget()->setLayout(applayout);
922  //Now enable/disable the shortcut buttons
923  ui->tool_browse_app->setVisible(false);
924  ui->tool_browse_cat->setVisible(true);
925  ui->tool_browse_gotocat->setVisible(false);
926    QStringList catinfo = PBI->CatInfo(cat,QStringList() << "name" << "icon");
927    ui->tool_browse_cat->setText(catinfo[0]);
928    if(catinfo[1].isEmpty()){ catinfo[1] = defaultIcon; }
929    ui->tool_browse_cat->setIcon(QIcon(catinfo[1]));
930  ui->tabWidget->setCurrentWidget(ui->tab_browse);
931  ui->stacked_browser->setCurrentWidget(ui->page_cat);
932  //Now save that this category is currently displayed
933  cCat = cat;
934}
935
936void MainUI::slotGoToApp(QString appID){
937  qDebug() << "Show App:" << appID;
938  //Get the general application info
939  QStringList data; 
940  data << "name" << "icon" << "author" << "website" << "license" << "type" << "description" \
941       << "category" << "requiresroot" << "latestversion" << "latestarch" <<"latestsize" \
942       << "backupversion" << "backuparch" << "backupsize";
943  data = PBI->AppInfo(appID,data);
944  if(data.isEmpty()){
945    qDebug() << "Invalid App:" << appID;
946    return;
947  }
948  cApp = appID; //save this for later
949  //Start the search for similar apps
950  PBI->searchSimilar = appID;
951  ui->group_bapp_similar->setVisible(FALSE);
952  QTimer::singleShot(0,PBI,SLOT(startSimilarSearch()));
953  //Now Check icon
954  if(data[1].isEmpty()){ data[1] = defaultIcon; }
955  //Now fill the UI with the data
956  ui->label_bapp_name->setText(data[0]);
957  ui->label_bapp_icon->setPixmap(QPixmap(data[1]));
958  if(data[3].isEmpty()){ ui->label_bapp_authorweb->setText(data[2]); }
959  else{ ui->label_bapp_authorweb->setText("<a href="+data[3]+">"+data[2]+"</a>"); }
960  ui->label_bapp_authorweb->setToolTip(data[3]);
961  ui->label_bapp_license->setText(data[4]);
962  ui->label_bapp_type->setText(data[5]);
963  ui->text_bapp_description->setText(data[6]);
964  //Now determine the appropriate version info
965  QString pbiID = PBI->isInstalled(appID); //get pbiID
966  QString cVer;
967  if(!pbiID.isEmpty()){ cVer = PBI->PBIInfo(pbiID,QStringList("version")).join(""); }
968  bool useLatest=FALSE;
969  bool nobackup = data[12].isEmpty();
970  if(cVer.isEmpty()){ useLatest=TRUE; } //not currently installed
971  else if(cVer != data[9]){ useLatest=TRUE;} //not the latest version
972  //Now put the proper version info on the UI
973  if(useLatest || nobackup){
974    ui->label_bapp_version->setText(data[9]);
975    ui->label_bapp_arch->setText(data[10]);
976    if(data[11].isEmpty()){ ui->label_bapp_size->setText(tr("Unknown")); }
977    else{ ui->label_bapp_size->setText( Extras::sizeKToDisplay(data[11]) ); }
978  }else{
979    ui->label_bapp_version->setText(data[12]);
980    ui->label_bapp_arch->setText(data[13]);
981    if(data[14].isEmpty()){ ui->label_bapp_size->setText(tr("Unknown")); }
982    else{ ui->label_bapp_size->setText( Extras::sizeKToDisplay(data[14]) ); }
983  }
984  //Now update the download button appropriately
985  slotUpdateAppDownloadButton();
986
987  //Now enable/disable the shortcut buttons
988  ui->tool_browse_app->setVisible(TRUE);
989    ui->tool_browse_app->setText(data[0]);
990    ui->tool_browse_app->setIcon(QIcon(data[1]));
991  QStringList catinfo = PBI->CatInfo(Extras::nameToID(data[7]),QStringList() << "name" << "icon");
992  if(!catinfo.isEmpty()){
993    ui->tool_browse_gotocat->setVisible(false);
994    ui->tool_browse_cat->setVisible(TRUE);
995    ui->tool_browse_cat->setText(catinfo[0]);
996    if(catinfo[1].isEmpty()){ catinfo[1] = defaultIcon; }
997    ui->tool_browse_cat->setIcon(QIcon(catinfo[1]));
998  }
999  ui->tabWidget->setCurrentWidget(ui->tab_browse);
1000  ui->stacked_browser->setCurrentWidget(ui->page_app);
1001       
1002}
1003
1004void MainUI::slotUpdateAppDownloadButton(){
1005  QString ico;
1006  QString working = PBI->currentAppStatus(cApp);
1007  QString rawstat = PBI->currentAppStatus(cApp, true);
1008  QStringList info = PBI->AppInfo(cApp, QStringList() << "latestversion" << "backupversion" << "requiresroot");
1009  QString pbiID = PBI->isInstalled(cApp);
1010  //qDebug() << "App Download status:" << working << rawstat;
1011  if(!working.isEmpty() && !rawstat.isEmpty() ){ //app currently pending or actually doing something
1012    if(rawstat.startsWith("DLSTAT::")){ ui->tool_bapp_download->setText(tr("Downloading..")); }
1013    else{ ui->tool_bapp_download->setText(working); }
1014    ui->tool_bapp_download->setIcon(QIcon(":icons/working.png"));
1015    ui->tool_bapp_download->setEnabled(FALSE);
1016  }else if( pbiID.isEmpty() ){ //new installation
1017    ui->tool_bapp_download->setText(tr("Install Now!"));
1018    ico = ":icons/app_download.png";
1019    ui->tool_bapp_download->setEnabled(TRUE);
1020  }else if( !PBI->upgradeAvailable(pbiID).isEmpty() ){ //Update available
1021    ui->tool_bapp_download->setText(tr("Update"));
1022    ico = ":icons/app_upgrade.png";
1023    ui->tool_bapp_download->setEnabled(TRUE);
1024  }else if(!info[1].isEmpty()){  //Downgrade available
1025    ui->tool_bapp_download->setText(tr("Downgrade"));
1026    ico = ":icons/app_downgrade.png";
1027    ui->tool_bapp_download->setEnabled(TRUE);
1028  }else{ //already installed (no downgrade available)
1029    ui->tool_bapp_download->setText(tr("Installed"));
1030    ui->tool_bapp_download->setIcon(QIcon(":icons/dialog-ok.png"));
1031    ui->tool_bapp_download->setEnabled(FALSE);
1032  }
1033  //Now set the icon appropriately if it requires root permissions
1034  if(!ico.isEmpty()){
1035    if(info[2]=="true"){ //requires root permissions to install
1036      ico.replace(".png","-root.png");
1037    }
1038    ui->tool_bapp_download->setIcon(QIcon(ico));
1039  }
1040  ui->tool_bapp_download->setWhatsThis(cApp); //set for slot
1041}
1042
1043void MainUI::slotGoToSearch(){
1044  searchTimer->stop(); //just in case "return" was pressed to start the search
1045  QString search = ui->line_browse_searchbar->text();
1046  if(search.isEmpty()){ return; }
1047  PBI->searchTerm = search;
1048  QTimer::singleShot(1,PBI,SLOT(startAppSearch()));
1049  ui->label_bsearch_info->setText( tr("Searching the application database. Please Wait....") );
1050    ui->label_bsearch_info->setVisible(TRUE);
1051    ui->group_bsearch_best->setVisible(FALSE);
1052    ui->group_bsearch_other->setVisible(FALSE);
1053 
1054}
1055       
1056void MainUI::slotShowSimilarApps(QStringList apps){
1057  qDebug() << " - Similar applications:" << apps;
1058  if(apps.isEmpty()){ ui->group_bapp_similar->setVisible(FALSE); }
1059  else{
1060    clearScrollArea(ui->scroll_bapp_similar);
1061    QHBoxLayout *layout = new QHBoxLayout;
1062    for(int i=0; i<apps.length(); i++){
1063      QStringList appdata = PBI->AppInfo(apps[i],QStringList() << "name" << "icon");
1064      if(!appdata.isEmpty()){
1065        SmallItemWidget *item = new SmallItemWidget(apps[i],appdata[0],appdata[1],"");
1066        connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToApp(QString)) );
1067        layout->addWidget(item);
1068      }
1069    }
1070    layout->addStretch(); //add a spacer to the end
1071    layout->setContentsMargins(1,1,1,1);
1072    ui->scroll_bapp_similar->widget()->setLayout(layout);
1073    //Make sure that the similar scrollarea is the proper fit vertically (no vertical scrolling)
1074    ui->scroll_bapp_similar->setMinimumHeight(ui->scroll_bapp_similar->widget()->minimumSizeHint().height() +ui->scroll_bapp_similar->horizontalScrollBar()->height()/1.2);
1075    //Now make the group visible as appropriate
1076    ui->group_bapp_similar->setVisible(TRUE);
1077    if(ui->group_bapp_similar->isChecked()){ ui->scroll_bapp_similar->setVisible(TRUE); }
1078    else{ ui->scroll_bapp_similar->setVisible(FALSE); }
1079    //Now make sure the app page has the proper layout dimensions
1080    ui->page_app->updateGeometry();
1081  }
1082}
1083
1084void MainUI::slotShowSearchResults(QStringList best, QStringList rest){
1085  //Now display the search results
1086  if(best.isEmpty()){
1087    ui->label_bsearch_info->setText( QString(tr("No Search Results Found for the term: %1")).arg(ui->line_browse_searchbar->text()) );
1088    ui->label_bsearch_info->setVisible(TRUE);
1089    ui->group_bsearch_best->setVisible(FALSE);
1090    ui->group_bsearch_other->setVisible(FALSE);
1091  }else{
1092    ui->label_bsearch_info->setVisible(FALSE);
1093    //Now fill the "best" section
1094    clearScrollArea(ui->scroll_bsearch_best);
1095    QVBoxLayout *layout = new QVBoxLayout;
1096    QStringList info; info << "name" << "shortdescription" << "icon" << "type";
1097    for(int i=0; i<best.length(); i++){
1098      QStringList data = PBI->AppInfo(best[i],info);
1099      if(!data.isEmpty()){
1100        //Trim the description if necessary
1101        if(data[1].size()>100){ data[1] = data[1].left(100).append("..."); }
1102        LargeItemWidget *item = new LargeItemWidget(best[i],data[0],data[1],data[2]);
1103        item->setType(data[3].toLower());
1104        connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToApp(QString)) );
1105        layout->addWidget(item); 
1106      }
1107    }
1108    layout->addStretch();
1109    ui->scroll_bsearch_best->widget()->setLayout(layout);
1110    ui->group_bsearch_best->setVisible(TRUE);
1111   
1112    //Now fill the other results
1113    if(rest.isEmpty()){ ui->group_bsearch_other->setVisible(FALSE); }
1114    else{
1115      ui->group_bsearch_other->setVisible(TRUE);
1116      clearScrollArea(ui->scroll_bsearch_other);
1117      QVBoxLayout *layout2 = new QVBoxLayout;
1118      for(int i=0; i<rest.length(); i++){
1119        QStringList data = PBI->AppInfo(rest[i],info);
1120        if(!data.isEmpty()){
1121          //Trim the description if necessary
1122          if(data[1].size()>100){ data[1] = data[1].left(100).append("..."); }
1123          LargeItemWidget *item = new LargeItemWidget(rest[i],data[0],data[1],data[2]);
1124          item->setType(data[3].toLower());
1125          connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToApp(QString)) );
1126          layout2->addWidget(item); 
1127        }
1128      }
1129      layout2->addStretch();
1130      ui->scroll_bsearch_other->widget()->setLayout(layout2);
1131    }
1132  }
1133       
1134  //need to make sure the search bar still has keyboard focus (just in case)
1135  ui->tabWidget->setCurrentWidget(ui->tab_browse);
1136  ui->stacked_browser->setCurrentWidget(ui->page_search);
1137 
1138}
1139
1140void MainUI::on_tabWidget_currentChanged(){
1141  if(ui->tabWidget->currentWidget() == ui->tab_browse){
1142    //Refresh the app page if that is the one currently showing
1143    if(ui->stacked_browser->currentWidget() == ui->page_app){ on_tool_browse_app_clicked(); }     
1144  }else{
1145    //Always return to the installed list
1146    ui->stackedWidget->setCurrentWidget(ui->page_install_list);
1147  }
1148}
1149
1150void MainUI::on_tool_browse_home_clicked(){
1151  slotGoToHome();
1152}
1153
1154void MainUI::on_tool_browse_cat_clicked(){
1155  QString cat = Extras::nameToID(ui->tool_browse_cat->text());
1156  if(cCat == cat){ //already loaded - just show it (prevents resetting scroll positions)
1157    ui->stacked_browser->setCurrentWidget(ui->page_cat);
1158  }else{ //load and show the category
1159    slotGoToCategory(cat);
1160  }
1161}
1162
1163void MainUI::on_tool_browse_app_clicked(){
1164  QString appID = Extras::nameToID(ui->tool_browse_app->text());
1165  slotGoToApp(appID);
1166}
1167
1168void MainUI::on_line_browse_searchbar_textChanged(){
1169  //Connect this to a singleshot timer, so the search functionality is only
1170  //  run once after a short wait rather than every time a new character is typed
1171 
1172  //Live search only after 3 characters have been typed
1173  if(ui->line_browse_searchbar->text().length() > 2){
1174    searchTimer->start();
1175  }else{
1176    searchTimer->stop();         
1177  }
1178}
1179
1180void MainUI::on_tool_bapp_download_clicked(){
1181  QString appID = ui->tool_bapp_download->whatsThis();
1182  //Verify the app installation
1183  if( QMessageBox::Yes != QMessageBox::question(this,tr("Verify Installation"), tr("Are you sure you want to install this application?")+"\n\n"+appID,QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)){
1184    return;
1185  }
1186  PBI->installApp(QStringList() << appID);
1187  ui->tool_bapp_download->setEnabled(FALSE); //make sure it cannot be clicked more than once before page refresh
1188  //Now show the Installed tab
1189  //ui->tabWidget->setCurrentWidget(ui->tab_installed);
1190}
1191
1192void MainUI::on_group_br_home_newapps_toggled(bool show){
1193  ui->scroll_br_home_newapps->setVisible(show);
1194}
1195
1196void MainUI::on_group_bapp_similar_toggled(bool show){
1197  ui->scroll_bapp_similar->setVisible(show);
1198}
1199
1200// ================================
1201// ==========  OTHER ==============
1202// ================================
1203void MainUI::clearScrollArea(QScrollArea* area){
1204  QWidget *wgt = area->takeWidget();
1205  delete wgt; //delete the widget and all children
1206  area->setWidget( new QWidget() ); //create a new widget in the scroll area
1207  area->widget()->setContentsMargins(0,0,0,0);
1208}
1209
1210void MainUI::slotDisplayError(QString title,QString message,QStringList log){
1211  QMessageBox *dlg = new QMessageBox(this);
1212    dlg->setWindowTitle(title);
1213    dlg->setText(message);
1214    dlg->setDetailedText(log.join("\n"));
1215  dlg->exec();
1216}
1217
1218void MainUI::slotDisplayStats(){
1219  int avail = PBI->numAvailable;
1220  int installed = PBI->numInstalled;
1221  QString text;
1222  if(avail == -1){
1223    text = QString(tr("Installed: %1")).arg(QString::number(installed)); 
1224  }else{
1225    text = QString(tr("Installed: %1")+"\t"+tr("Available: %2")).arg(QString::number(installed), QString::number(avail)); 
1226  }
1227  //Get the number of installed/available applications and display it
1228  statusLabel->setText(text);   
1229}
Note: See TracBrowser for help on using the repository browser.