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

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

Clean up a couple more backup functions in the AppCafe?:
1) If you try to remove a non-installed PBI, it will simply cancel the installation instead
2) Make sure that if a non-installed PBI is right-clicked, it only shows the "cancel" option.

  • Property mode set to 100644
File size: 53.4 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 if actually installed
540  if( !PBI->isInstalled(pbiID).isEmpty() ){
541    QMenu *pmenu = contextActionMenu->addMenu( QIcon(":icons/xdg_paths.png"), tr("Path Links"));
542      pmenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddPath()) );
543      pmenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemovePath()) ); 
544      pmenu->addAction( QIcon(":icons/add-root.png"),tr("Add (All Users)"),this,SLOT(slotActionAddPathAll()) );
545  }
546  if(info[2]=="true"){
547    QMenu *fmenu = contextActionMenu->addMenu( QIcon(":icons/xdg_mime.png"), tr("File Associations"));
548      fmenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddMime()) );
549      fmenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemoveMime()) ); 
550      fmenu->addAction( QIcon(":icons/add-root.png"),tr("Add (All Users)"),this,SLOT(slotActionAddMimeAll()) );
551  }
552  if(!pending){
553    //Remove option is only available if not currently pending actions
554    contextActionMenu->addSeparator();
555    contextActionMenu->addAction( QIcon(":icons/remove.png"), tr("Uninstall"), this, SLOT(slotActionRemove()) );
556  }
557  if(pending){
558    //Cancel option is only available if actions are currently pending   
559    contextActionMenu->addSeparator();
560    contextActionMenu->addAction( QIcon(":icons/dialog-cancel.png"), tr("Cancel Actions"), this, SLOT(slotActionCancel()) );
561  }
562  //Now show the menu
563  cDetails = pbiID; //save this so we know which app is currently being modified
564  contextActionMenu->popup(ui->tree_install_apps->mapToGlobal(pt));
565}
566
567void MainUI::contextMenuFinished(){
568  QTimer::singleShot(500, this, SLOT(slotCheckSelectedItems()) );       
569}
570
571// === SELECTED PBI ACTIONS ===
572void MainUI::slotActionAddDesktop(){
573  QStringList items = getCheckedItems();
574  PBI->addDesktopIcons(items,FALSE); //only for current user
575}
576
577void MainUI::slotActionRemoveDesktop(){
578  QStringList items = getCheckedItems();
579  PBI->rmDesktopIcons(items,FALSE);  //Only for current user
580}
581
582void MainUI::slotActionAddPath(){
583  QStringList items = getCheckedItems();
584  PBI->addPaths(items,FALSE);  //Only for current user 
585}
586
587void MainUI::slotActionRemovePath(){
588  QStringList items = getCheckedItems();
589  PBI->rmPaths(items,FALSE);  //Only for current user
590}
591
592void MainUI::slotActionAddPathAll(){
593  QStringList items = getCheckedItems();
594  PBI->addPaths(items,TRUE);  //For all users (root permissions required)
595}
596
597void MainUI::slotActionAddMenu(){
598  QStringList items = getCheckedItems();
599  PBI->addMenuIcons(items,FALSE);  //Only for current user
600}
601
602void MainUI::slotActionRemoveMenu(){
603  QStringList items = getCheckedItems();
604  PBI->rmMenuIcons(items,FALSE);  //Only for current user       
605}
606
607void MainUI::slotActionAddMenuAll(){
608  QStringList items = getCheckedItems();
609  PBI->addMenuIcons(items,TRUE);  //For all users (root permissions required)   
610}
611
612void MainUI::slotActionAddMime(){
613  QStringList items = getCheckedItems();
614  PBI->addMimeTypes(items,FALSE);  //Only for current user     
615}
616
617void MainUI::slotActionRemoveMime(){
618  QStringList items = getCheckedItems();
619  PBI->rmMimeTypes(items,FALSE);  //Only for current user       
620}
621
622void MainUI::slotActionAddMimeAll(){
623    QStringList items = getCheckedItems();
624  PBI->addMimeTypes(items,TRUE);  //For all users (root permissions required)   
625}
626
627void MainUI::slotActionUpdate(){
628  QStringList checkedID = getCheckedItems();
629  if(!checkedID.isEmpty()){
630    PBI->upgradePBI(checkedID); 
631  }
632}
633
634void MainUI::slotActionRemove(){
635  QStringList checkedID = getCheckedItems();
636  if(!checkedID.isEmpty()){
637    //Verify that the user really wants to remove these apps
638    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) ){
639      PBI->removePBI(checkedID);
640    }
641  }
642}
643
644void MainUI::slotActionCancel(){
645  QStringList checkedID = getCheckedItems();
646  if(!checkedID.isEmpty()){
647    PBI->cancelActions(checkedID); 
648  }
649}
650
651void MainUI::slotStartApp(QAction* act){
652  qDebug() << "Starting external application:" << act->text();
653  //Get the command from the action
654  QString desktopfile = act->whatsThis();
655  QString cmd = "xdg-open "+desktopfile;
656  //Startup the command externally
657  QProcess::execute(cmd);
658}
659
660void MainUI::slotUpdateSelectedPBI(){
661  //Get the currently selected app
662  QString appID;
663  if(ui->tree_install_apps->topLevelItemCount() > 0){
664    appID = ui->tree_install_apps->currentItem()->whatsThis(0);
665  }
666  //See if this one is actually installed
667  appID = PBI->isInstalled(appID);
668  if(appID.isEmpty()){
669    ui->tool_install_details->setEnabled(false);
670  }else{
671    ui->tool_install_details->setEnabled(true);   
672  }
673   
674}
675
676void MainUI::updateInstallDetails(QString appID){
677  //Get the information to update the details page
678  //Get the PBI info for that item
679    cDetails = appID; //save for later
680    QStringList vals; 
681    vals << "name" << "icon" << "author" << "website" << "version" << "license" << "description" << "maintainer" << "date" << "arch";
682    QStringList bools;
683    bools << "autoupdate" << "hasdesktopicons" << "hasmenuicons" << "requiresroot" << "hasmimetypes";
684    vals = PBI->PBIInfo(appID,vals);
685    bools = PBI->PBIInfo(appID,bools);
686    //Make sure the info lists are not empty
687    if(vals.isEmpty() || bools.isEmpty()){
688      ui->tool_install_details->setEnabled(false);
689      return; 
690    }
691    //Load a default icon if none found
692    if(vals[1].isEmpty()){ vals[1] = defaultIcon; }
693    //Now set the info on the UI
694    bool desktopSC = (bools[1] == "true"); // XDG desktop entries available
695    bool menuSC= (bools[2] == "true");   // XDG menu entries available
696    bool autoupdate = (bools[0] == "true"); //auto-update enabled
697    bool rootonly = (bools[3] == "true"); //requires root
698    bool mimeSC = (bools[4] == "true"); //XDG mime entries available
699    //Create the shortcuts string
700    sDeskMenu->setEnabled(desktopSC);
701    sMenuMenu->setEnabled(menuSC);
702    sMimeMenu->setEnabled(mimeSC);
703    //Now display that info on the UI
704    ui->label_install_app->setText(vals[0]);
705    ui->label_install_icon->setPixmap( QPixmap(vals[1]).scaled(64,64, Qt::KeepAspectRatio, Qt::SmoothTransformation) );
706    if(vals[3].isEmpty()){ 
707      ui->label_install_author->setText(vals[2]); 
708      ui->label_install_author->setToolTip("");
709    }else{ 
710      ui->label_install_author->setText("<a href="+vals[3]+">"+vals[2]+"</a>"); 
711      ui->label_install_author->setToolTip(vals[3]); //show website URL as tooltip
712    }
713    ui->label_install_version->setText(vals[4]);
714    ui->label_install_license->setText(vals[5]);
715    ui->text_install_description->setPlainText(vals[6]);
716    ui->tool_install_maintainer->setVisible( vals[7].contains("@") );
717    ui->label_install_date->setText(vals[8]);
718    ui->label_install_arch->setText(vals[9]);
719    //ui->label_install_shortcuts->setText(shortcuts);
720    ui->check_install_autoupdate->setChecked(autoupdate);
721 
722    //Adjust the quick action buttons as necessary
723    if( PBI->isWorking(appID) ){
724      //Actions pending/working only show cancel button
725      ui->tool_install_cancel->setVisible(TRUE);
726      ui->tool_install_remove->setVisible(FALSE);
727      ui->tool_install_update->setVisible(FALSE);
728      ui->tool_install_startApp->setVisible(FALSE);
729    }else{
730      //Nothing pending
731      ui->tool_install_cancel->setVisible(FALSE);
732      if( !PBI->isInstalled(appID).isEmpty() ){ 
733        //Remove Button
734        ui->tool_install_remove->setVisible(TRUE);
735        if(rootonly){ ui->tool_install_remove->setIcon(QIcon(":icons/remove-root.png")); }
736        else{ ui->tool_install_remove->setIcon(QIcon(":icons/remove.png")); }
737        //Update
738        if(PBI->upgradeAvailable(appID).isEmpty()){ ui->tool_install_update->setVisible(FALSE); }
739        else{
740          ui->tool_install_update->setVisible(TRUE); 
741          if(rootonly){ ui->tool_install_update->setIcon(QIcon(":icons/app_upgrade_small-root.png")); }
742          else{ ui->tool_install_update->setIcon(QIcon(":icons/app_upgrade_small.png")); }
743        }
744        //Start Application binaries
745        QStringList bins = PBI->pbiBinList(appID);
746        appBinMenu->clear();
747        for(int i=0; i<bins.length(); i++){
748          QAction *act = new QAction(this);
749            act->setText(bins[i].section("::::",0,0)); //set name
750            act->setWhatsThis(bins[i].section("::::",1,10)); //set command string
751          appBinMenu->addAction(act);
752        }
753        if(appBinMenu->isEmpty()){ ui->tool_install_startApp->setVisible(FALSE); }
754        else{ ui->tool_install_startApp->setVisible(TRUE); }
755      }else{ 
756        ui->tool_install_remove->setVisible(FALSE); 
757        ui->tool_install_update->setVisible(FALSE); 
758        ui->tool_install_startApp->setVisible(FALSE);
759      }   
760    }
761
762  //Update the current status indicators
763  QString stat = PBI->currentAppStatus(appID,true); //get the raw status
764  QString statF = PBI->currentAppStatus(appID, false); //get the non-raw status
765  if( stat.isEmpty() && statF.isEmpty() ){
766    //Not currently running - hide the display indicators
767    ui->group_install_appStat->setVisible(false);
768  }else{
769    //Currently installing/removing/updating - show last message from process
770    if(!statF.isEmpty()){ ui->label_install_status->setText(statF); }
771    else{ ui->label_install_status->setText(stat); }
772    ui->group_install_appStat->setVisible(TRUE);
773  }
774}
775
776// ==========================
777// ====== BROWSER TAB =======
778// ==========================
779void MainUI::initializeBrowserTab(){
780  ui->tab_browse->setEnabled(FALSE);
781  //Always make sure that the browser starts off on the "home" page
782  ui->stacked_browser->setCurrentWidget(ui->page_home);
783  //With invisible shortcut buttons
784  ui->tool_browse_cat->setVisible(FALSE);
785  ui->tool_browse_app->setVisible(FALSE);
786  //Clear any items left over from the designer form
787  clearScrollArea(ui->scroll_br_home_newapps);
788  clearScrollArea(ui->scroll_br_home_rec);
789  //Search functionality
790  searchTimer = new QTimer();
791    searchTimer->setSingleShot(TRUE);
792    searchTimer->setInterval(300); // 0.3 sec wait before a search
793    connect(searchTimer,SIGNAL(timeout()),this,SLOT(slotGoToSearch()) );
794  connect(ui->tool_browse_search,SIGNAL(clicked()),this,SLOT(slotGoToSearch()) );
795  connect(ui->line_browse_searchbar,SIGNAL(returnPressed()),this,SLOT(slotGoToSearch()) );
796  connect(ui->tool_browse_gotocat, SIGNAL(clicked()), this, SLOT(slotGoToCatBrowser()) );
797}
798
799// === SLOTS ===
800void MainUI::slotDisableBrowser(bool shownotification){
801  if(shownotification){ qDebug() << "No Repo Available: De-activating the Browser"; }
802  ui->tabWidget->setCurrentWidget(ui->tab_installed);
803  ui->tab_browse->setEnabled(FALSE);
804  slotDisplayStats();
805}
806
807void MainUI::slotEnableBrowser(){
808  qDebug() << "Repo Ready: - generating browser home page";
809  //Now create the browser home page
810  slotUpdateBrowserHome();
811  //And allow the user to go there
812  ui->tab_browse->setEnabled(TRUE);
813  slotDisplayStats();
814}
815
816void MainUI::slotUpdateBrowserHome(){
817  //Load the Recommendations
818  clearScrollArea(ui->scroll_br_home_rec);
819  QVBoxLayout *reclayout = new QVBoxLayout;
820  QStringList recList = PBI->getRecommendations();
821  QStringList info; info << "name" << "shortdescription" << "icon" << "type";
822  for(int i=0; i<recList.length(); i++){
823    QStringList data = PBI->AppInfo(recList[i],info);
824    if(!data.isEmpty()){
825      LargeItemWidget *item = new LargeItemWidget(recList[i],data[0],data[1],data[2]);
826      //Set the type icon
827      item->setType(data[3].toLower());
828      connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToApp(QString)) );
829      reclayout->addWidget(item);
830    }
831  }
832  reclayout->addStretch(); //add a spacer to the end
833  ui->scroll_br_home_rec->widget()->setLayout(reclayout);
834  //Load the newest applications
835  clearScrollArea(ui->scroll_br_home_newapps);
836  QHBoxLayout *newapplayout = new QHBoxLayout;
837  QStringList newapps = PBI->getRecentApps();
838  for(int i=0; i<newapps.length(); i++){
839    QStringList appdata = PBI->AppInfo(newapps[i],QStringList() << "name" << "icon" << "latestversion");
840    if(!appdata.isEmpty()){
841      SmallItemWidget *item = new SmallItemWidget(newapps[i],appdata[0],appdata[1],appdata[2]);
842      connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToApp(QString)) );
843      newapplayout->addWidget(item);
844    }
845  }
846  newapplayout->addStretch(); //add a spacer to the end
847  newapplayout->setContentsMargins(0,0,0,0);
848  newapplayout->setSpacing(0);
849  ui->scroll_br_home_newapps->widget()->setLayout(newapplayout);
850  //Make sure that the newapps scrollarea is the proper fit vertically (no vertical scrolling)
851  ui->scroll_br_home_newapps->setMinimumHeight(ui->scroll_br_home_newapps->widget()->minimumSizeHint().height());
852 
853  //Make sure the new apps area is invisible if no items available
854  if(newapps.isEmpty()){ ui->group_br_home_newapps->setVisible(FALSE); }
855  else{ ui->group_br_home_newapps->setVisible(TRUE); }
856  //make sure the home page is visible in the browser (slotGoToHome without changing tabs)
857  ui->stacked_browser->setCurrentWidget(ui->page_home); 
858  //Make sure the shortcut buttons are disabled
859  ui->tool_browse_cat->setVisible(FALSE);
860  ui->tool_browse_app->setVisible(FALSE); 
861 
862  //Now update the category browser page (since it only needs to be done once like the home menu)
863  //Load the Categories
864  QStringList cats = PBI->browserCategories();
865    cats.sort();
866    clearScrollArea(ui->scroll_br_cats);
867    QVBoxLayout *catlayout = new QVBoxLayout;
868    info.clear(); info << "name" << "description" << "icon";
869    for(int i=0; i<cats.length(); i++){
870      QStringList data = PBI->CatInfo(cats[i],info);
871      if(!data.isEmpty()){
872        LargeItemWidget *item = new LargeItemWidget(cats[i],data[0],data[1],data[2]);
873        connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToCategory(QString)) );
874        catlayout->addWidget(item);
875      }
876    }
877    catlayout->addStretch(); //add a spacer to the end
878    ui->scroll_br_cats->widget()->setLayout(catlayout);
879}
880
881void MainUI::slotGoToHome(){
882  ui->tabWidget->setCurrentWidget(ui->tab_browse);
883  ui->stacked_browser->setCurrentWidget(ui->page_home); 
884  //Make sure the shortcut buttons are disabled
885  ui->tool_browse_cat->setVisible(false);
886  ui->tool_browse_app->setVisible(false);
887  ui->tool_browse_gotocat->setVisible(true);
888}
889
890void MainUI::slotGoToCatBrowser(){
891  ui->tabWidget->setCurrentWidget(ui->tab_browse);
892  ui->stacked_browser->setCurrentWidget(ui->page_browsecats);
893  //Make sure the shortcut buttons are diabled
894  ui->tool_browse_cat->setVisible(false);
895  ui->tool_browse_app->setVisible(false);
896  ui->tool_browse_gotocat->setVisible(false);
897}
898
899void MainUI::slotGoToCategory(QString cat){
900  qDebug() << "Show Category:" << cat;
901  //Get the apps in this category
902  QStringList apps = PBI->browserApps(cat);
903  if(apps.isEmpty()){
904    qDebug() << "Invalid category:" << cat;
905    return;
906  }
907  clearScrollArea(ui->scroll_br_cat_apps);
908  apps.sort();
909  QVBoxLayout *applayout = new QVBoxLayout;
910  QStringList info; info << "name" << "shortdescription" << "icon" << "type";
911  for(int i=0; i<apps.length(); i++){
912    QStringList data = PBI->AppInfo(apps[i],info);
913    if(!data.isEmpty()){
914      //Trim the description if necessary
915      if(data[1].size()>100){ data[1] = data[1].left(100).append("..."); }
916      LargeItemWidget *item = new LargeItemWidget(apps[i],data[0],data[1],data[2]);
917      item->setType(data[3].toLower());
918      connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToApp(QString)) );
919      applayout->addWidget(item); 
920    }
921  }
922  applayout->addStretch();
923  ui->scroll_br_cat_apps->widget()->setLayout(applayout);
924  //Now enable/disable the shortcut buttons
925  ui->tool_browse_app->setVisible(false);
926  ui->tool_browse_cat->setVisible(false);
927  ui->tool_browse_gotocat->setVisible(true);
928  //Setup the icon/name for the category display
929    QStringList catinfo = PBI->CatInfo(cat,QStringList() << "name" << "icon");
930    ui->tool_browse_cat->setText(catinfo[0]);
931    ui->label_cat_name->setText(catinfo[0]);
932    if(catinfo[1].isEmpty()){ catinfo[1] = defaultIcon; }
933    ui->tool_browse_cat->setIcon(QIcon(catinfo[1]));
934    ui->label_cat_icon->setPixmap( QPixmap(catinfo[1]).scaled(32,32) ); 
935  //Now move to the page
936  ui->tabWidget->setCurrentWidget(ui->tab_browse);
937  ui->stacked_browser->setCurrentWidget(ui->page_cat);
938  //Now save that this category is currently displayed
939  cCat = cat;
940}
941
942void MainUI::slotGoToApp(QString appID){
943  qDebug() << "Show App:" << appID;
944  //Get the general application info
945  QStringList data; 
946  data << "name" << "icon" << "author" << "website" << "license" << "type" << "description" \
947       << "category" << "requiresroot" << "latestversion" << "latestarch" <<"latestsize" \
948       << "backupversion" << "backuparch" << "backupsize";
949  data = PBI->AppInfo(appID,data);
950  if(data.isEmpty()){
951    qDebug() << "Invalid App:" << appID;
952    return;
953  }
954  cApp = appID; //save this for later
955  //Start the search for similar apps
956  PBI->searchSimilar = appID;
957  ui->group_bapp_similar->setVisible(FALSE);
958  QTimer::singleShot(0,PBI,SLOT(startSimilarSearch()));
959  //Now Check icon
960  if(data[1].isEmpty()){ data[1] = defaultIcon; }
961  //Now fill the UI with the data
962  ui->label_bapp_name->setText(data[0]);
963  ui->label_bapp_icon->setPixmap(QPixmap(data[1]));
964  if(data[3].isEmpty()){ ui->label_bapp_authorweb->setText(data[2]); }
965  else{ ui->label_bapp_authorweb->setText("<a href="+data[3]+">"+data[2]+"</a>"); }
966  ui->label_bapp_authorweb->setToolTip(data[3]);
967  ui->label_bapp_license->setText(data[4]);
968  ui->label_bapp_type->setText(data[5]);
969  ui->text_bapp_description->setText(data[6]);
970  //Now determine the appropriate version info
971  QString pbiID = PBI->isInstalled(appID); //get pbiID
972  QString cVer;
973  if(!pbiID.isEmpty()){ cVer = PBI->PBIInfo(pbiID,QStringList("version")).join(""); }
974  bool useLatest=FALSE;
975  bool nobackup = data[12].isEmpty();
976  if(cVer.isEmpty()){ useLatest=TRUE; } //not currently installed
977  else if(cVer != data[9]){ useLatest=TRUE;} //not the latest version
978  //Now put the proper version info on the UI
979  if(useLatest || nobackup){
980    ui->label_bapp_version->setText(data[9]);
981    ui->label_bapp_arch->setText(data[10]);
982    if(data[11].isEmpty()){ ui->label_bapp_size->setText(tr("Unknown")); }
983    else{ ui->label_bapp_size->setText( Extras::sizeKToDisplay(data[11]) ); }
984  }else{
985    ui->label_bapp_version->setText(data[12]);
986    ui->label_bapp_arch->setText(data[13]);
987    if(data[14].isEmpty()){ ui->label_bapp_size->setText(tr("Unknown")); }
988    else{ ui->label_bapp_size->setText( Extras::sizeKToDisplay(data[14]) ); }
989  }
990  //Now update the download button appropriately
991  slotUpdateAppDownloadButton();
992
993  //Now enable/disable the shortcut buttons
994  ui->tool_browse_app->setVisible(TRUE);
995    ui->tool_browse_app->setText(data[0]);
996    ui->tool_browse_app->setIcon(QIcon(data[1]));
997  QStringList catinfo = PBI->CatInfo(Extras::nameToID(data[7]),QStringList() << "name" << "icon");
998  if(!catinfo.isEmpty()){
999    ui->tool_browse_gotocat->setVisible(false);
1000    ui->tool_browse_cat->setVisible(TRUE);
1001    ui->tool_browse_cat->setText(catinfo[0]);
1002    if(catinfo[1].isEmpty()){ catinfo[1] = defaultIcon; }
1003    ui->tool_browse_cat->setIcon(QIcon(catinfo[1]));
1004  }
1005  ui->tabWidget->setCurrentWidget(ui->tab_browse);
1006  ui->stacked_browser->setCurrentWidget(ui->page_app);
1007       
1008}
1009
1010void MainUI::slotUpdateAppDownloadButton(){
1011  QString ico;
1012  QString working = PBI->currentAppStatus(cApp);
1013  QString rawstat = PBI->currentAppStatus(cApp, true);
1014  QStringList info = PBI->AppInfo(cApp, QStringList() << "latestversion" << "backupversion" << "requiresroot");
1015  QString pbiID = PBI->isInstalled(cApp);
1016  //qDebug() << "App Download status:" << working << rawstat;
1017  if(!working.isEmpty() && !rawstat.isEmpty() ){ //app currently pending or actually doing something
1018    if(rawstat.startsWith("DLSTAT::")){ ui->tool_bapp_download->setText(tr("Downloading..")); }
1019    else{ ui->tool_bapp_download->setText(working); }
1020    ui->tool_bapp_download->setIcon(QIcon(":icons/working.png"));
1021    ui->tool_bapp_download->setEnabled(FALSE);
1022  }else if( pbiID.isEmpty() ){ //new installation
1023    ui->tool_bapp_download->setText(tr("Install Now!"));
1024    ico = ":icons/app_download.png";
1025    ui->tool_bapp_download->setEnabled(TRUE);
1026  }else if( !PBI->upgradeAvailable(pbiID).isEmpty() ){ //Update available
1027    ui->tool_bapp_download->setText(tr("Update"));
1028    ico = ":icons/app_upgrade.png";
1029    ui->tool_bapp_download->setEnabled(TRUE);
1030  }else if(!info[1].isEmpty()){  //Downgrade available
1031    ui->tool_bapp_download->setText(tr("Downgrade"));
1032    ico = ":icons/app_downgrade.png";
1033    ui->tool_bapp_download->setEnabled(TRUE);
1034  }else{ //already installed (no downgrade available)
1035    ui->tool_bapp_download->setText(tr("Installed"));
1036    ui->tool_bapp_download->setIcon(QIcon(":icons/dialog-ok.png"));
1037    ui->tool_bapp_download->setEnabled(FALSE);
1038  }
1039  //Now set the icon appropriately if it requires root permissions
1040  if(!ico.isEmpty()){
1041    if(info[2]=="true"){ //requires root permissions to install
1042      ico.replace(".png","-root.png");
1043    }
1044    ui->tool_bapp_download->setIcon(QIcon(ico));
1045  }
1046  ui->tool_bapp_download->setWhatsThis(cApp); //set for slot
1047}
1048
1049void MainUI::slotGoToSearch(){
1050  searchTimer->stop(); //just in case "return" was pressed to start the search
1051  QString search = ui->line_browse_searchbar->text();
1052  if(search.isEmpty()){ return; }
1053  PBI->searchTerm = search;
1054  QTimer::singleShot(1,PBI,SLOT(startAppSearch()));
1055  ui->label_bsearch_info->setText( tr("Searching the application database. Please Wait....") );
1056    ui->label_bsearch_info->setVisible(TRUE);
1057    ui->group_bsearch_best->setVisible(FALSE);
1058    ui->group_bsearch_other->setVisible(FALSE);
1059 
1060}
1061       
1062void MainUI::slotShowSimilarApps(QStringList apps){
1063  qDebug() << " - Similar applications:" << apps;
1064  if(apps.isEmpty()){ ui->group_bapp_similar->setVisible(FALSE); }
1065  else{
1066    clearScrollArea(ui->scroll_bapp_similar);
1067    QHBoxLayout *layout = new QHBoxLayout;
1068    for(int i=0; i<apps.length(); i++){
1069      QStringList appdata = PBI->AppInfo(apps[i],QStringList() << "name" << "icon");
1070      if(!appdata.isEmpty()){
1071        SmallItemWidget *item = new SmallItemWidget(apps[i],appdata[0],appdata[1],"");
1072        connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToApp(QString)) );
1073        layout->addWidget(item);
1074      }
1075    }
1076    layout->addStretch(); //add a spacer to the end
1077    layout->setContentsMargins(1,1,1,1);
1078    ui->scroll_bapp_similar->widget()->setLayout(layout);
1079    //Make sure that the similar scrollarea is the proper fit vertically (no vertical scrolling)
1080    ui->scroll_bapp_similar->setMinimumHeight(ui->scroll_bapp_similar->widget()->minimumSizeHint().height() +ui->scroll_bapp_similar->horizontalScrollBar()->height()/1.2);
1081    //Now make the group visible as appropriate
1082    ui->group_bapp_similar->setVisible(TRUE);
1083    if(ui->group_bapp_similar->isChecked()){ ui->scroll_bapp_similar->setVisible(TRUE); }
1084    else{ ui->scroll_bapp_similar->setVisible(FALSE); }
1085    //Now make sure the app page has the proper layout dimensions
1086    ui->page_app->updateGeometry();
1087  }
1088}
1089
1090void MainUI::slotShowSearchResults(QStringList best, QStringList rest){
1091  //Now display the search results
1092  if(best.isEmpty()){
1093    ui->label_bsearch_info->setText( QString(tr("No Search Results Found for the term: %1")).arg(ui->line_browse_searchbar->text()) );
1094    ui->label_bsearch_info->setVisible(TRUE);
1095    ui->group_bsearch_best->setVisible(FALSE);
1096    ui->group_bsearch_other->setVisible(FALSE);
1097  }else{
1098    ui->label_bsearch_info->setVisible(FALSE);
1099    //Now fill the "best" section
1100    clearScrollArea(ui->scroll_bsearch_best);
1101    QVBoxLayout *layout = new QVBoxLayout;
1102    QStringList info; info << "name" << "shortdescription" << "icon" << "type";
1103    for(int i=0; i<best.length(); i++){
1104      QStringList data = PBI->AppInfo(best[i],info);
1105      if(!data.isEmpty()){
1106        //Trim the description if necessary
1107        if(data[1].size()>100){ data[1] = data[1].left(100).append("..."); }
1108        LargeItemWidget *item = new LargeItemWidget(best[i],data[0],data[1],data[2]);
1109        item->setType(data[3].toLower());
1110        connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToApp(QString)) );
1111        layout->addWidget(item); 
1112      }
1113    }
1114    layout->addStretch();
1115    ui->scroll_bsearch_best->widget()->setLayout(layout);
1116    ui->group_bsearch_best->setVisible(TRUE);
1117   
1118    //Now fill the other results
1119    if(rest.isEmpty()){ ui->group_bsearch_other->setVisible(FALSE); }
1120    else{
1121      ui->group_bsearch_other->setVisible(TRUE);
1122      clearScrollArea(ui->scroll_bsearch_other);
1123      QVBoxLayout *layout2 = new QVBoxLayout;
1124      for(int i=0; i<rest.length(); i++){
1125        QStringList data = PBI->AppInfo(rest[i],info);
1126        if(!data.isEmpty()){
1127          //Trim the description if necessary
1128          if(data[1].size()>100){ data[1] = data[1].left(100).append("..."); }
1129          LargeItemWidget *item = new LargeItemWidget(rest[i],data[0],data[1],data[2]);
1130          item->setType(data[3].toLower());
1131          connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToApp(QString)) );
1132          layout2->addWidget(item); 
1133        }
1134      }
1135      layout2->addStretch();
1136      ui->scroll_bsearch_other->widget()->setLayout(layout2);
1137    }
1138  }
1139       
1140  //need to make sure the search bar still has keyboard focus (just in case)
1141  ui->tabWidget->setCurrentWidget(ui->tab_browse);
1142  ui->stacked_browser->setCurrentWidget(ui->page_search);
1143 
1144}
1145
1146void MainUI::on_tabWidget_currentChanged(){
1147  if(ui->tabWidget->currentWidget() == ui->tab_browse){
1148    //Refresh the app page if that is the one currently showing
1149    if(ui->stacked_browser->currentWidget() == ui->page_app){ on_tool_browse_app_clicked(); }     
1150  }else{
1151    //Always return to the installed list
1152    ui->stackedWidget->setCurrentWidget(ui->page_install_list);
1153  }
1154}
1155
1156void MainUI::on_tool_browse_home_clicked(){
1157  slotGoToHome();
1158}
1159
1160void MainUI::on_tool_browse_cat_clicked(){
1161  QString cat = Extras::nameToID(ui->tool_browse_cat->text());
1162  if(cCat == cat){ //already loaded - just show it (prevents resetting scroll positions)
1163    ui->stacked_browser->setCurrentWidget(ui->page_cat);
1164    //Now enable/disable the shortcut buttons
1165    ui->tool_browse_app->setVisible(false);
1166    ui->tool_browse_cat->setVisible(false);
1167    ui->tool_browse_gotocat->setVisible(true);
1168  }else{ //load and show the category
1169    slotGoToCategory(cat);
1170  }
1171}
1172
1173void MainUI::on_tool_browse_app_clicked(){
1174  QString appID = Extras::nameToID(ui->tool_browse_app->text());
1175  slotGoToApp(appID);
1176}
1177
1178void MainUI::on_line_browse_searchbar_textChanged(){
1179  //Connect this to a singleshot timer, so the search functionality is only
1180  //  run once after a short wait rather than every time a new character is typed
1181 
1182  //Live search only after 3 characters have been typed
1183  if(ui->line_browse_searchbar->text().length() > 2){
1184    searchTimer->start();
1185  }else{
1186    searchTimer->stop();         
1187  }
1188}
1189
1190void MainUI::on_tool_bapp_download_clicked(){
1191  QString appID = ui->tool_bapp_download->whatsThis();
1192  //Verify the app installation
1193  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)){
1194    return;
1195  }
1196  PBI->installApp(QStringList() << appID);
1197  ui->tool_bapp_download->setEnabled(FALSE); //make sure it cannot be clicked more than once before page refresh
1198  //Now show the Installed tab
1199  ui->tabWidget->setCurrentWidget(ui->tab_installed);
1200}
1201
1202void MainUI::on_group_br_home_newapps_toggled(bool show){
1203  ui->scroll_br_home_newapps->setVisible(show);
1204}
1205
1206void MainUI::on_group_bapp_similar_toggled(bool show){
1207  ui->scroll_bapp_similar->setVisible(show);
1208}
1209
1210// ================================
1211// ==========  OTHER ==============
1212// ================================
1213void MainUI::clearScrollArea(QScrollArea* area){
1214  QWidget *wgt = area->takeWidget();
1215  delete wgt; //delete the widget and all children
1216  area->setWidget( new QWidget() ); //create a new widget in the scroll area
1217  area->widget()->setContentsMargins(0,0,0,0);
1218}
1219
1220void MainUI::slotDisplayError(QString title,QString message,QStringList log){
1221  QMessageBox *dlg = new QMessageBox(this);
1222    dlg->setWindowTitle(title);
1223    dlg->setText(message);
1224    dlg->setDetailedText(log.join("\n"));
1225  dlg->exec();
1226}
1227
1228void MainUI::slotDisplayStats(){
1229  int avail = PBI->numAvailable;
1230  int installed = PBI->numInstalled;
1231  QString text;
1232  if(avail == -1){
1233    text = QString(tr("Installed: %1")).arg(QString::number(installed)); 
1234  }else{
1235    text = QString(tr("Installed: %1")+"\t"+tr("Available: %2")).arg(QString::number(installed), QString::number(avail)); 
1236  }
1237  //Get the number of installed/available applications and display it
1238  statusLabel->setText(text);   
1239}
Note: See TracBrowser for help on using the repository browser.