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

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

Fix up the browser app page status notifications with the new backend process class.

  • Property mode set to 100644
File size: 48.1 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  //Setup the action menu for installed applications
148  actionMenu = new QMenu();
149    actionMenu->addAction( QIcon(":icons/view-refresh.png"), tr("Update"), this, SLOT(slotActionUpdate()) );
150    actionMenu->addSeparator();
151    QMenu *dmenu = actionMenu->addMenu( QIcon(":icons/xdg_desktop.png"), tr("Desktop Icons"));
152      dmenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddDesktop()) );
153      dmenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemoveDesktop()) );
154    QMenu *mmenu = actionMenu->addMenu( QIcon(":icons/xdg_menu.png"), tr("Menu Icons"));
155      mmenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddMenu()) );
156      mmenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemoveMenu()) ); 
157      mmenu->addAction( QIcon(":icons/add-root.png"),tr("Add (All Users)"),this,SLOT(slotActionAddMenuAll()) );
158    QMenu *pmenu = actionMenu->addMenu( QIcon(":icons/xdg_paths.png"), tr("Path Links"));
159      pmenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddPath()) );
160      pmenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemovePath()) ); 
161      pmenu->addAction( QIcon(":icons/add-root.png"),tr("Add (All Users)"),this,SLOT(slotActionAddPathAll()) );
162    QMenu *fmenu = actionMenu->addMenu( QIcon(":icons/xdg_mime.png"), tr("File Associations"));
163      fmenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddMime()) );
164      fmenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemoveMime()) ); 
165      fmenu->addAction( QIcon(":icons/add-root.png"),tr("Add (All Users)"),this,SLOT(slotActionAddMimeAll()) );
166    actionMenu->addSeparator();
167    actionMenu->addAction( QIcon(":icons/remove.png"), tr("Uninstall"), this, SLOT(slotActionRemove()) );
168    actionMenu->addSeparator();
169    actionMenu->addAction( QIcon(":icons/dialog-cancel.png"), tr("Cancel Actions"), this, SLOT(slotActionCancel()) );
170  //Setup the shortcuts menu for installed applications
171  shortcutMenu = new QMenu(this);
172    sDeskMenu = shortcutMenu->addMenu( QIcon(":icons/xdg_desktop.png"), tr("Desktop Icons"));
173      sDeskMenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddDesktop()) );
174      sDeskMenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemoveDesktop()) );
175    sMenuMenu = shortcutMenu->addMenu( QIcon(":icons/xdg_menu.png"), tr("Menu Icons"));
176      sMenuMenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddMenu()) );
177      sMenuMenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemoveMenu()) ); 
178      sMenuMenu->addAction( QIcon(":icons/add-root.png"),tr("Add (All Users)"),this,SLOT(slotActionAddMenuAll()) );
179    QMenu *spmenu = shortcutMenu->addMenu( QIcon(":icons/xdg_paths.png"), tr("Path Links"));
180      spmenu->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddPath()) );
181      spmenu->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemovePath()) ); 
182      spmenu->addAction( QIcon(":icons/add-root.png"),tr("Add (All Users)"),this,SLOT(slotActionAddPathAll()) );
183    sMimeMenu = shortcutMenu->addMenu( QIcon(":icons/xdg_mime.png"), tr("File Associations"));
184      sMimeMenu ->addAction( QIcon(":icons/add.png"),tr("Add"),this,SLOT(slotActionAddMime()) );
185      sMimeMenu ->addAction( QIcon(":icons/remove.png"),tr("Remove"),this,SLOT(slotActionRemoveMime()) ); 
186      sMimeMenu ->addAction( QIcon(":icons/add-root.png"),tr("Add (All Users)"),this,SLOT(slotActionAddMimeAll()) );
187  ui->tool_install_shortcuts->setMenu(shortcutMenu);
188  //Setup the binary menu for installed applications
189  appBinMenu = new QMenu();
190  ui->tool_install_startApp->setMenu(appBinMenu);
191    connect(appBinMenu, SIGNAL(triggered(QAction*)), this, SLOT(slotStartApp(QAction*)) );
192  //Now setup the action button
193  ui->tool_install_performaction->setMenu(actionMenu);
194  ui->tool_install_performaction->setPopupMode(QToolButton::InstantPopup);
195  //Now setup any defaults for the installed tab
196  ui->tree_install_apps->setIconSize(QSize(22,22));
197  connect(ui->tree_install_apps, SIGNAL(itemClicked(QTreeWidgetItem*, int)), this, SLOT(slotCheckSelectedItems()) );
198  slotRefreshInstallTab();
199}
200
201void MainUI::formatInstalledItemDisplay(QTreeWidgetItem *item){
202  //simplification function for filling the tree widget item with the appropriate information about the PBI
203  QString ID = item->whatsThis(0);
204  if(item->text(0).isEmpty()){  //new entry - get everything
205    QStringList vals; vals <<"name"<<"version"<<"status"; //same as column order
206    if(!vals.isEmpty()){
207      vals = PBI->PBIInfo(ID, vals);
208      for(int i=0; i<vals.length(); i++){ item->setText(i,vals[i]); }
209      QString icon = PBI->PBIInfo(ID, QStringList("icon")).join("");
210        //Load a default icon if none found
211      if(icon.isEmpty()){ icon = defaultIcon; }
212      item->setIcon(0,QIcon(icon) );
213      item->setCheckState(0,Qt::Unchecked);
214    }
215  }else{ // Just update the necesary info
216    QStringList vals; vals << "version" << "status";
217    vals = PBI->PBIInfo(ID,vals);
218    if(!vals.isEmpty()){ item->setText(1,vals[0]); item->setText(2,vals[1]); }
219  }
220}
221
222QStringList MainUI::getCheckedItems(){
223  //Return the pbiID's of all the active items
224  QStringList output;
225  //See if we are on the single-app details page - then get the current app only
226  if(ui->stackedWidget->currentWidget() == ui->page_install_details){
227      output << cDetails; 
228         
229  //If on the main Installed page, look for checked items only
230  }else{
231    for(int i=0; i<ui->tree_install_apps->topLevelItemCount(); i++){
232      if(ui->tree_install_apps->topLevelItem(i)->checkState(0) == Qt::Checked){
233        output << ui->tree_install_apps->topLevelItem(i)->whatsThis(0);
234      }
235    }
236  }
237  //qDebug() << "Checked Items:" << output;
238  return output;       
239}
240
241// === SLOTS ===
242void MainUI::slotRefreshInstallTab(){
243  //Update the list of installed PBI's w/o clearing the list (loses selections)
244   //Get the list we need (in order)
245  QStringList installList = PBI->installedList();
246  installList.sort(); //sort in ascending order
247   //Get the list we have now
248  QStringList cList;
249  for(int i=0; i<ui->tree_install_apps->topLevelItemCount(); i++){
250    cList << ui->tree_install_apps->topLevelItem(i)->whatsThis(0);
251  }
252  //Quick finish if no items installed
253  if(installList.isEmpty()){
254    ui->tree_install_apps->clear();
255  }else{
256    //Now make adjustments as necessary
257    for(int i=0; i<installList.length(); i++){
258      //Detemine what action should be done with this item location
259      int todo = 0; //0=insert new item, 1=update current item, 2=remove current item
260      if(i < cList.length()){
261        if(installList[i] == cList[i]){ todo=1; }
262        else if( installList.contains(cList[i]) && !cList.contains(installList[i]) ){ todo=0; } //new item to be inserted here
263        else if( !installList.contains(cList[i]) ){ todo=2; } //current item in this location should be removed
264      }
265      //Now perform the action on this location
266      if(todo==0){ 
267        //insert new item
268        QTreeWidgetItem *item = new QTreeWidgetItem; //create the item
269        item->setWhatsThis(0,installList[i]);
270        //Now format the display
271        formatInstalledItemDisplay(item);
272        if(item->text(0).isEmpty()){
273          //Do not put empty items into the display
274          delete item;
275        }else{
276          //Now insert this item onto the list
277          ui->tree_install_apps->insertTopLevelItem(i,item);
278          cList.insert(i,installList[i]); //reflect this inclusion into the current list
279        }
280      }else if(todo==1){
281        //Update current item
282        formatInstalledItemDisplay( ui->tree_install_apps->topLevelItem(i) );
283      }else{
284        //Remove current item
285        ui->tree_install_apps->takeTopLevelItem(i);
286        cList.removeAt(i); //reflect the change to the current list
287        i--; //Re-check the item that should be in this location
288      }
289    }
290    //Now makesure that there are no extra items at the end
291    int il = installList.length();
292    while(il < cList.length()){
293      ui->tree_install_apps->takeTopLevelItem(il);
294      cList.removeAt(il); //reflect the change to the current list
295    }
296  } //end of empty list check
297 
298  //Make sure that there is an item selected
299  if(ui->tree_install_apps->topLevelItemCount() > 0 ){
300    if( ui->tree_install_apps->selectedItems().isEmpty() ){
301      ui->tree_install_apps->setCurrentItem( ui->tree_install_apps->topLevelItem(0) );
302    }
303    //Now re-size the columns to the minimum required width
304    for(int i=0; i<3; i++){
305      ui->tree_install_apps->resizeColumnToContents(i);
306    } 
307  }
308  slotUpdateSelectedPBI();; //Update the info boxes
309  slotDisplayStats();
310  slotCheckSelectedItems();
311  //If the browser app page is currently visible for this app
312  if( (ui->stacked_browser->currentWidget() == ui->page_app) && ui->page_app->isVisible() ){
313    slotGoToApp(cApp);
314  }
315}
316
317void MainUI::slotCheckSelectedItems(){
318  bool chkd = false;
319  for(int i=0; i<ui->tree_install_apps->topLevelItemCount(); i++){
320    if(ui->tree_install_apps->topLevelItem(i)->checkState(0) == Qt::Checked){
321      chkd = true; break;
322    }
323  }
324  ui->tool_install_performaction->setEnabled(chkd);
325}
326
327void MainUI::slotPBIStatusUpdate(QString pbiID){
328  //This will do a full update of a particlar PBI entry
329  //    and just update/check the icons for all the other PBI's
330  for(int i=0; i<ui->tree_install_apps->topLevelItemCount(); i++){
331    QString itemID = ui->tree_install_apps->topLevelItem(i)->whatsThis(0);
332    if(itemID == pbiID){
333      QString stat = PBI->PBIInfo(pbiID,QStringList()<<"status").join("");
334      ui->tree_install_apps->topLevelItem(i)->setText(2,stat);
335      // See if we need to update the download progress bar
336      QString appID = ui->tree_install_apps->currentItem()->whatsThis(0);
337      if ( appID == pbiID ) {
338        slotUpdateSelectedPBI();
339        //If the details page is currently visible, update it too
340        if(ui->stackedWidget->currentWidget() == ui->page_install_details){
341          updateInstallDetails(appID);
342        }
343      }
344    }else{
345      //Just check/update the icon if necessary
346      if(ui->tree_install_apps->topLevelItem(i)->icon(0).isNull()){
347         ui->tree_install_apps->topLevelItem(i)->setIcon(0, QIcon( PBI->PBIInfo(itemID, QStringList() << "icon").join("") ));
348      }
349    }
350  }
351  //If the browser app page is current for this app
352  QString metaID = PBI->pbiToAppID(pbiID);
353  if( (ui->stacked_browser->currentWidget() == ui->page_app) && (cApp == metaID) && ui->page_app->isVisible() ){
354    slotUpdateAppDownloadButton();
355  }
356}
357
358void MainUI::on_tool_install_details_clicked(){
359  //Get the current item
360  QString appID;
361  if(ui->tree_install_apps->topLevelItemCount() > 0){
362    appID = ui->tree_install_apps->currentItem()->whatsThis(0);
363  }
364  if(appID.isEmpty()){return;}
365  //Update the info on the details page
366  updateInstallDetails(appID);
367  //Now show the page
368  ui->stackedWidget->setCurrentWidget(ui->page_install_details);
369}
370
371void MainUI::on_tool_install_back_clicked(){
372  //List page should always be current based upon backend
373  ui->stackedWidget->setCurrentWidget(ui->page_install_list);
374}
375
376void MainUI::on_tool_install_gotobrowserpage_clicked(){
377  //When you want to open up the browser page for an application
378  QString appID = Extras::nameToID( ui->tree_install_apps->currentItem()->text(0) );
379  slotGoToApp(appID);
380}
381
382void MainUI::on_tool_install_toggleall_clicked(){
383  //Determine if the current item is checked or unchecked
384  bool checkall = (ui->tree_install_apps->currentItem()->checkState(0) == Qt::Unchecked);
385  for(int i=0; i<ui->tree_install_apps->topLevelItemCount(); i++){
386    if(checkall){ ui->tree_install_apps->topLevelItem(i)->setCheckState(0,Qt::Checked); }
387    else{ui->tree_install_apps->topLevelItem(i)->setCheckState(0,Qt::Unchecked); }
388  }
389  slotCheckSelectedItems();
390}
391
392void MainUI::on_tree_install_apps_itemSelectionChanged(){
393  //When an installed PBI is clicked on
394  slotUpdateSelectedPBI();
395  if(ui->stackedWidget->currentWidget() != ui->page_install_list){
396    ui->stackedWidget->setCurrentWidget(ui->page_install_list);
397  }
398  slotCheckSelectedItems();
399}
400
401void MainUI::on_tree_install_apps_itemDoubleClicked(QTreeWidgetItem *item){
402 //Make sure it is a valid/installed application
403 QString appID = item->whatsThis(0);
404  appID = PBI->isInstalled(appID);
405  if(appID.isEmpty()){ return; } //invalid item
406  //Update the info on the details page
407  updateInstallDetails(appID);
408  //Now show the page
409  ui->stackedWidget->setCurrentWidget(ui->page_install_details);
410}
411
412void MainUI::on_check_install_autoupdate_clicked(){
413  //Get the current item
414  QString appID;
415  if(ui->tree_install_apps->topLevelItemCount() > 0){
416    appID = ui->tree_install_apps->currentItem()->whatsThis(0);
417  }
418  if(appID.isEmpty()){return;}
419  //Now determine the current state of the checkbox
420  bool enabled = ui->check_install_autoupdate->isChecked();
421  //Now have the backend make the change
422  PBI->enableAutoUpdate(appID, enabled);
423  //Now ask if the user also wants to start updating it now
424  if(enabled && !PBI->upgradeAvailable(appID).isEmpty()){
425    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) ){
426      PBI->upgradePBI(QStringList() << appID);
427    }
428  }
429  //Now force the info on the page to be updated in a moment (need time for database to update)
430  QTimer::singleShot(200, this, SLOT(on_tool_install_details_clicked()) );
431}
432
433void MainUI::on_tool_install_update_clicked(){
434  //Get the current item
435  QString appID;
436  if(ui->tree_install_apps->topLevelItemCount() > 0){
437    appID = ui->tree_install_apps->currentItem()->whatsThis(0);
438  }
439  if(appID.isEmpty()){return;}
440  PBI->upgradePBI(QStringList() << appID);
441}
442
443void MainUI::on_tool_install_remove_clicked(){
444  //Get the current item
445  QString appID;
446  if(ui->tree_install_apps->topLevelItemCount() > 0){
447    appID = ui->tree_install_apps->currentItem()->whatsThis(0);
448  }
449  if(appID.isEmpty()){return;}
450  //Verify removal
451  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) ){
452    PBI->removePBI(QStringList() << appID);
453  }
454}
455
456void MainUI::on_tool_install_cancel_clicked(){
457  //Get the current item
458  QString appID;
459  if(ui->tree_install_apps->topLevelItemCount() > 0){
460    appID = ui->tree_install_apps->currentItem()->whatsThis(0);
461  }
462  if(appID.isEmpty()){return;}
463  PBI->cancelActions(QStringList() << appID);
464 
465}
466
467void MainUI::on_tool_install_maintainer_clicked(){
468  //Get the current item
469  QString appID;
470  if(ui->tree_install_apps->topLevelItemCount() > 0){
471    appID = ui->tree_install_apps->currentItem()->whatsThis(0);
472  }
473  if(appID.isEmpty()){return;}
474  //Get the maintainer email
475  QString email = PBI->PBIInfo(appID, QStringList() << "maintainer").join("");
476  if(email.isEmpty()){ return; }
477  qDebug() << "Launching email to:" << email;
478  //Get the command from the action
479  QString cmd = "mailto:"+email;
480  QStringList info = PBI->PBIInfo(appID, QStringList() << "name" << "date" << "version" << "arch" << "fbsdversion" );
481  //Add a sample subject
482  cmd.append("?subject="+info[0]+" port question");
483  //Add the info to the body of the email
484  cmd.append("&body=");
485  cmd.append("-----------\nPBI Information:\nName: "+info[0] + "\nDate Created: "+info[1] +"\nVersion: "+info[2] +"\nArchitecture: "+info[3] +"\nFreeBSD Version: "+info[4] );
486  //Startup the command externally
487  QProcess::execute("xdg-open \""+cmd+"\"");
488}
489
490// === SELECTED PBI ACTIONS ===
491void MainUI::slotActionAddDesktop(){
492  QStringList items = getCheckedItems();
493  PBI->addDesktopIcons(items,FALSE); //only for current user
494}
495
496void MainUI::slotActionRemoveDesktop(){
497  QStringList items = getCheckedItems();
498  PBI->rmDesktopIcons(items,FALSE);  //Only for current user
499}
500
501void MainUI::slotActionAddPath(){
502  QStringList items = getCheckedItems();
503  PBI->addPaths(items,FALSE);  //Only for current user 
504}
505
506void MainUI::slotActionRemovePath(){
507  QStringList items = getCheckedItems();
508  PBI->rmPaths(items,FALSE);  //Only for current user
509}
510
511void MainUI::slotActionAddPathAll(){
512  QStringList items = getCheckedItems();
513  PBI->addPaths(items,TRUE);  //For all users (root permissions required)
514}
515
516void MainUI::slotActionAddMenu(){
517  QStringList items = getCheckedItems();
518  PBI->addMenuIcons(items,FALSE);  //Only for current user
519}
520
521void MainUI::slotActionRemoveMenu(){
522  QStringList items = getCheckedItems();
523  PBI->rmMenuIcons(items,FALSE);  //Only for current user       
524}
525
526void MainUI::slotActionAddMenuAll(){
527  QStringList items = getCheckedItems();
528  PBI->addMenuIcons(items,TRUE);  //For all users (root permissions required)   
529}
530
531void MainUI::slotActionAddMime(){
532  QStringList items = getCheckedItems();
533  PBI->addMimeTypes(items,FALSE);  //Only for current user     
534}
535
536void MainUI::slotActionRemoveMime(){
537  QStringList items = getCheckedItems();
538  PBI->rmMimeTypes(items,FALSE);  //Only for current user       
539}
540
541void MainUI::slotActionAddMimeAll(){
542    QStringList items = getCheckedItems();
543  PBI->addMimeTypes(items,TRUE);  //For all users (root permissions required)   
544}
545
546void MainUI::slotActionUpdate(){
547  QStringList checkedID = getCheckedItems();
548  if(!checkedID.isEmpty()){
549    PBI->upgradePBI(checkedID); 
550  }
551}
552
553void MainUI::slotActionRemove(){
554  QStringList checkedID = getCheckedItems();
555  if(!checkedID.isEmpty()){
556    //Verify that the user really wants to remove these apps
557    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) ){
558      PBI->removePBI(checkedID);
559    }
560  }
561}
562
563void MainUI::slotActionCancel(){
564  QStringList checkedID = getCheckedItems();
565  if(!checkedID.isEmpty()){
566    PBI->cancelActions(checkedID); 
567  }
568}
569
570void MainUI::slotStartApp(QAction* act){
571  qDebug() << "Starting external application:" << act->text();
572  //Get the command from the action
573  QString desktopfile = act->whatsThis();
574  QString cmd = "xdg-open "+desktopfile;
575  //Startup the command externally
576  QProcess::execute(cmd);
577}
578
579void MainUI::slotUpdateSelectedPBI(){
580  //Get the currently selected app
581  QString appID;
582  if(ui->tree_install_apps->topLevelItemCount() > 0){
583    appID = ui->tree_install_apps->currentItem()->whatsThis(0);
584  }
585  //See if this one is actually installed
586  appID = PBI->isInstalled(appID);
587  if(appID.isEmpty()){
588    ui->tool_install_details->setEnabled(false);
589  }else{
590    ui->tool_install_details->setEnabled(true);   
591  }
592   
593}
594
595void MainUI::updateInstallDetails(QString appID){
596  //Get the information to update the details page
597  //Get the PBI info for that item
598    cDetails = appID; //save for later
599    QStringList vals; 
600    vals << "name" << "icon" << "author" << "website" << "version" << "license" << "description" << "maintainer" << "date" << "arch";
601    QStringList bools;
602    bools << "autoupdate" << "hasdesktopicons" << "hasmenuicons" << "requiresroot" << "hasmimetypes";
603    vals = PBI->PBIInfo(appID,vals);
604    bools = PBI->PBIInfo(appID,bools);
605    //Make sure the info lists are not empty
606    if(vals.isEmpty() || bools.isEmpty()){
607      ui->tool_install_details->setEnabled(false);
608      return; 
609    }
610    //Load a default icon if none found
611    if(vals[1].isEmpty()){ vals[1] = defaultIcon; }
612    //Now set the info on the UI
613    bool desktopSC = (bools[1] == "true"); // XDG desktop entries available
614    bool menuSC= (bools[2] == "true");   // XDG menu entries available
615    bool autoupdate = (bools[0] == "true"); //auto-update enabled
616    bool rootonly = (bools[3] == "true"); //requires root
617    bool mimeSC = (bools[4] == "true"); //XDG mime entries available
618    //Create the shortcuts string
619    sDeskMenu->setEnabled(desktopSC);
620    sMenuMenu->setEnabled(menuSC);
621    sMimeMenu->setEnabled(mimeSC);
622    //Now display that info on the UI
623    ui->label_install_app->setText(vals[0]);
624    ui->label_install_icon->setPixmap( QPixmap(vals[1]).scaled(64,64, Qt::KeepAspectRatio, Qt::SmoothTransformation) );
625    if(vals[3].isEmpty()){ 
626      ui->label_install_author->setText(vals[2]); 
627      ui->label_install_author->setToolTip("");
628    }else{ 
629      ui->label_install_author->setText("<a href="+vals[3]+">"+vals[2]+"</a>"); 
630      ui->label_install_author->setToolTip(vals[3]); //show website URL as tooltip
631    }
632    ui->label_install_version->setText(vals[4]);
633    ui->label_install_license->setText(vals[5]);
634    ui->text_install_description->setPlainText(vals[6]);
635    ui->tool_install_maintainer->setVisible( vals[7].contains("@") );
636    ui->label_install_date->setText(vals[8]);
637    ui->label_install_arch->setText(vals[9]);
638    //ui->label_install_shortcuts->setText(shortcuts);
639    ui->check_install_autoupdate->setChecked(autoupdate);
640 
641    //Adjust the quick action buttons as necessary
642    if( PBI->isWorking(appID) ){
643      //Actions pending/working only show cancel button
644      ui->tool_install_cancel->setVisible(TRUE);
645      ui->tool_install_remove->setVisible(FALSE);
646      ui->tool_install_update->setVisible(FALSE);
647      ui->tool_install_startApp->setVisible(FALSE);
648    }else{
649      //Nothing pending
650      ui->tool_install_cancel->setVisible(FALSE);
651      if( !PBI->isInstalled(appID).isEmpty() ){ 
652        //Remove Button
653        ui->tool_install_remove->setVisible(TRUE);
654        if(rootonly){ ui->tool_install_remove->setIcon(QIcon(":icons/remove-root.png")); }
655        else{ ui->tool_install_remove->setIcon(QIcon(":icons/remove.png")); }
656        //Update
657        if(PBI->upgradeAvailable(appID).isEmpty()){ ui->tool_install_update->setVisible(FALSE); }
658        else{
659          ui->tool_install_update->setVisible(TRUE); 
660          if(rootonly){ ui->tool_install_update->setIcon(QIcon(":icons/app_upgrade_small-root.png")); }
661          else{ ui->tool_install_update->setIcon(QIcon(":icons/app_upgrade_small.png")); }
662        }
663        //Start Application binaries
664        QStringList bins = PBI->pbiBinList(appID);
665        appBinMenu->clear();
666        for(int i=0; i<bins.length(); i++){
667          QAction *act = new QAction(this);
668            act->setText(bins[i].section("::::",0,0)); //set name
669            act->setWhatsThis(bins[i].section("::::",1,10)); //set command string
670          appBinMenu->addAction(act);
671        }
672        if(appBinMenu->isEmpty()){ ui->tool_install_startApp->setVisible(FALSE); }
673        else{ ui->tool_install_startApp->setVisible(TRUE); }
674      }else{ 
675        ui->tool_install_remove->setVisible(FALSE); 
676        ui->tool_install_update->setVisible(FALSE); 
677        ui->tool_install_startApp->setVisible(FALSE);
678      }   
679    }
680
681  //Update the current status indicators
682  QString stat = PBI->currentAppStatus(appID,true); //get the raw status
683  QString statF = PBI->currentAppStatus(appID, false); //get the non-raw status
684  if( stat.isEmpty() && statF.isEmpty() ){
685    //Not currently running - hide the display indicators
686    ui->group_install_appStat->setVisible(false);
687  /*}else if(stat.startsWith("DLSTAT::")){
688    //Currently downloading - show download status indicators
689    QString percent = stat.section("::",1,1);
690    QString total = stat.section("::",2,2);
691    QString speed = stat.section("::",3,3);
692    ui->group_install_appStat->setVisible(TRUE);
693      ui->progress_install_DL->setVisible(TRUE);
694    if(total == "??"){ ui->label_install_status->setText( tr("Downloading file:") ); }
695    else{ ui->label_install_status->setText( QString(tr("Downloading %1 file:")).arg(total) ); }
696    if(percent == "??"){
697      ui->progress_install_DL->setMinimum(0); ui->progress_install_DL->setMaximum(0);
698    }else{
699      ui->progress_install_DL->setMinimum(0); ui->progress_install_DL->setMaximum(1000);
700      ui->progress_install_DL->setValue( int(percent.toFloat()*10) );
701    }
702    if(speed == "??"){ ui->label_install_DL->setVisible(FALSE); }
703    else{
704      ui->label_install_DL->setVisible(TRUE);
705      ui->label_install_DL->setText(speed);
706    }*/
707  }else{
708    //Currently installing/removing/updating - show last message from process
709    if(!statF.isEmpty()){ ui->label_install_status->setText(statF); }
710    else{ ui->label_install_status->setText(stat); }
711    ui->group_install_appStat->setVisible(TRUE);
712      //ui->progress_install_DL->setVisible(FALSE);
713      //ui->label_install_DL->setVisible(FALSE);
714  }
715}
716
717// ==========================
718// ====== BROWSER TAB =======
719// ==========================
720void MainUI::initializeBrowserTab(){
721  ui->tab_browse->setEnabled(FALSE);
722  //Always make sure that the browser starts off on the "home" page
723  ui->stacked_browser->setCurrentWidget(ui->page_home);
724  //With invisible shortcut buttons
725  ui->tool_browse_cat->setVisible(FALSE);
726  ui->tool_browse_app->setVisible(FALSE);
727  //Clear any items left over from the designer form
728  clearScrollArea(ui->scroll_br_home_newapps);
729  clearScrollArea(ui->scroll_br_home_cats);
730  //Search functionality
731  searchTimer = new QTimer();
732    searchTimer->setSingleShot(TRUE);
733    searchTimer->setInterval(300); // 0.3 sec wait before a search
734    connect(searchTimer,SIGNAL(timeout()),this,SLOT(slotGoToSearch()) );
735  connect(ui->tool_browse_search,SIGNAL(clicked()),this,SLOT(slotGoToSearch()) );
736  connect(ui->line_browse_searchbar,SIGNAL(returnPressed()),this,SLOT(slotGoToSearch()) );
737}
738
739// === SLOTS ===
740void MainUI::slotDisableBrowser(bool shownotification){
741  if(shownotification){ qDebug() << "No Repo Available: De-activating the Browser"; }
742  ui->tabWidget->setCurrentWidget(ui->tab_installed);
743  ui->tab_browse->setEnabled(FALSE);
744  slotDisplayStats();
745}
746
747void MainUI::slotEnableBrowser(){
748  qDebug() << "Repo Ready: - generating browser home page";
749  //Now create the browser home page
750  slotUpdateBrowserHome();
751  //And allow the user to go there
752  ui->tab_browse->setEnabled(TRUE);
753  slotDisplayStats();
754}
755
756void MainUI::slotUpdateBrowserHome(){
757  //Load the Categories
758  QStringList cats = PBI->browserCategories();
759  clearScrollArea(ui->scroll_br_home_cats);
760  QVBoxLayout *catlayout = new QVBoxLayout;
761  QStringList info; info << "name" << "description" << "icon";
762  cats.sort(); //sort them alphabetically
763  for(int i=0; i<cats.length(); i++){
764    QStringList data = PBI->CatInfo(cats[i],info);
765    if(!data.isEmpty()){
766      LargeItemWidget *item = new LargeItemWidget(cats[i],data[0],data[1],data[2]);
767      connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToCategory(QString)) );
768      catlayout->addWidget(item);
769    }
770  }
771  catlayout->addStretch(); //add a spacer to the end
772  ui->scroll_br_home_cats->widget()->setLayout(catlayout);
773  //Load the newest applications
774  clearScrollArea(ui->scroll_br_home_newapps);
775  QHBoxLayout *newapplayout = new QHBoxLayout;
776  QStringList newapps = PBI->getRecentApps();
777  for(int i=0; i<newapps.length(); i++){
778    QStringList appdata = PBI->AppInfo(newapps[i],QStringList() << "name" << "icon" << "latestversion");
779    if(!appdata.isEmpty()){
780      SmallItemWidget *item = new SmallItemWidget(newapps[i],appdata[0],appdata[1],appdata[2]);
781      connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToApp(QString)) );
782      newapplayout->addWidget(item);
783    }
784  }
785  newapplayout->addStretch(); //add a spacer to the end
786  newapplayout->setContentsMargins(0,0,0,0);
787  newapplayout->setSpacing(0);
788  ui->scroll_br_home_newapps->widget()->setLayout(newapplayout);
789  //Make sure that the newapps scrollarea is the proper fit vertically (no vertical scrolling)
790  ui->scroll_br_home_newapps->setMinimumHeight(ui->scroll_br_home_newapps->widget()->minimumSizeHint().height()+ui->scroll_br_home_newapps->horizontalScrollBar()->height());
791 
792  //Make sure the new apps area is invisible if no items available
793  if(newapps.isEmpty()){ ui->group_br_home_newapps->setVisible(FALSE); }
794  else{ ui->group_br_home_newapps->setVisible(TRUE); }
795  //make sure the home page is visible in the browser (slotGoToHome without changing tabs)
796  ui->stacked_browser->setCurrentWidget(ui->page_home); 
797  //Make sure the shortcut buttons are disabled
798  ui->tool_browse_cat->setVisible(FALSE);
799  ui->tool_browse_app->setVisible(FALSE); 
800 
801}
802
803void MainUI::slotGoToHome(){
804  ui->tabWidget->setCurrentWidget(ui->tab_browse);
805  ui->stacked_browser->setCurrentWidget(ui->page_home); 
806  //Make sure the shortcut buttons are disabled
807  ui->tool_browse_cat->setVisible(FALSE);
808  ui->tool_browse_app->setVisible(FALSE);
809}
810
811void MainUI::slotGoToCategory(QString cat){
812  qDebug() << "Show Category:" << cat;
813  //Get the apps in this category
814  QStringList apps = PBI->browserApps(cat);
815  if(apps.isEmpty()){
816    qDebug() << "Invalid category:" << cat;
817    return;
818  }
819  clearScrollArea(ui->scroll_br_cat_apps);
820  apps.sort();
821  QVBoxLayout *applayout = new QVBoxLayout;
822  QStringList info; info << "name" << "shortdescription" << "icon";
823  for(int i=0; i<apps.length(); i++){
824    QStringList data = PBI->AppInfo(apps[i],info);
825    if(!data.isEmpty()){
826      //Trim the description if necessary
827      if(data[1].size()>100){ data[1] = data[1].left(100).append("..."); }
828      LargeItemWidget *item = new LargeItemWidget(apps[i],data[0],data[1],data[2]);
829      connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToApp(QString)) );
830      applayout->addWidget(item); 
831    }
832  }
833  applayout->addStretch();
834  ui->scroll_br_cat_apps->widget()->setLayout(applayout);
835  //Now enable/disable the shortcut buttons
836  ui->tool_browse_app->setVisible(FALSE);
837  ui->tool_browse_cat->setVisible(TRUE);
838    QStringList catinfo = PBI->CatInfo(cat,QStringList() << "name" << "icon");
839    ui->tool_browse_cat->setText(catinfo[0]);
840    if(catinfo[1].isEmpty()){ catinfo[1] = defaultIcon; }
841    ui->tool_browse_cat->setIcon(QIcon(catinfo[1]));
842  ui->tabWidget->setCurrentWidget(ui->tab_browse);
843  ui->stacked_browser->setCurrentWidget(ui->page_cat);
844  //Now save that this category is currently displayed
845  cCat = cat;
846}
847
848void MainUI::slotGoToApp(QString appID){
849  qDebug() << "Show App:" << appID;
850  //Get the general application info
851  QStringList data; 
852  data << "name" << "icon" << "author" << "website" << "license" << "type" << "description" \
853       << "category" << "requiresroot" << "latestversion" << "latestarch" <<"latestsize" \
854       << "backupversion" << "backuparch" << "backupsize";
855  data = PBI->AppInfo(appID,data);
856  if(data.isEmpty()){
857    qDebug() << "Invalid App:" << appID;
858    return;
859  }
860  cApp = appID; //save this for later
861  //Start the search for similar apps
862  PBI->searchSimilar = appID;
863  ui->group_bapp_similar->setVisible(FALSE);
864  QTimer::singleShot(0,PBI,SLOT(startSimilarSearch()));
865  //Now Check icon
866  if(data[1].isEmpty()){ data[1] = defaultIcon; }
867  //Now fill the UI with the data
868  ui->label_bapp_name->setText(data[0]);
869  ui->label_bapp_icon->setPixmap(QPixmap(data[1]));
870  if(data[3].isEmpty()){ ui->label_bapp_authorweb->setText(data[2]); }
871  else{ ui->label_bapp_authorweb->setText("<a href="+data[3]+">"+data[2]+"</a>"); }
872  ui->label_bapp_authorweb->setToolTip(data[3]);
873  ui->label_bapp_license->setText(data[4]);
874  ui->label_bapp_type->setText(data[5]);
875  ui->text_bapp_description->setText(data[6]);
876  //Now determine the appropriate version info
877  QString pbiID = PBI->isInstalled(appID); //get pbiID
878  QString cVer;
879  if(!pbiID.isEmpty()){ cVer = PBI->PBIInfo(pbiID,QStringList("version")).join(""); }
880  bool useLatest=FALSE;
881  bool nobackup = data[12].isEmpty();
882  if(cVer.isEmpty()){ useLatest=TRUE; } //not currently installed
883  else if(cVer != data[9]){ useLatest=TRUE;} //not the latest version
884  //Now put the proper version info on the UI
885  if(useLatest || nobackup){
886    ui->label_bapp_version->setText(data[9]);
887    ui->label_bapp_arch->setText(data[10]);
888    if(data[11].isEmpty()){ ui->label_bapp_size->setText(tr("Unknown")); }
889    else{ ui->label_bapp_size->setText( Extras::sizeKToDisplay(data[11]) ); }
890  }else{
891    ui->label_bapp_version->setText(data[12]);
892    ui->label_bapp_arch->setText(data[13]);
893    if(data[14].isEmpty()){ ui->label_bapp_size->setText(tr("Unknown")); }
894    else{ ui->label_bapp_size->setText( Extras::sizeKToDisplay(data[14]) ); }
895  }
896  //Now update the download button appropriately
897  slotUpdateAppDownloadButton();
898
899  //Now enable/disable the shortcut buttons
900  ui->tool_browse_app->setVisible(TRUE);
901    ui->tool_browse_app->setText(data[0]);
902    ui->tool_browse_app->setIcon(QIcon(data[1]));
903  QStringList catinfo = PBI->CatInfo(Extras::nameToID(data[7]),QStringList() << "name" << "icon");
904  if(!catinfo.isEmpty()){
905    ui->tool_browse_cat->setVisible(TRUE);
906    ui->tool_browse_cat->setText(catinfo[0]);
907    if(catinfo[1].isEmpty()){ catinfo[1] = defaultIcon; }
908    ui->tool_browse_cat->setIcon(QIcon(catinfo[1]));
909  }
910  ui->tabWidget->setCurrentWidget(ui->tab_browse);
911  ui->stacked_browser->setCurrentWidget(ui->page_app);
912       
913}
914
915void MainUI::slotUpdateAppDownloadButton(){
916  QString ico;
917  QString working = PBI->currentAppStatus(cApp);
918  QString rawstat = PBI->currentAppStatus(cApp, true);
919  QStringList info = PBI->AppInfo(cApp, QStringList() << "latestversion" << "backupversion" << "requiresroot");
920  QString pbiID = PBI->isInstalled(cApp);
921  qDebug() << "App Download status:" << working << rawstat;
922  if(!working.isEmpty() && !rawstat.isEmpty() ){ //app currently pending or actually doing something
923    if(rawstat.startsWith("DLSTAT::")){ ui->tool_bapp_download->setText(tr("Downloading..")); }
924    else{ ui->tool_bapp_download->setText(working); }
925    ui->tool_bapp_download->setIcon(QIcon(":icons/working.png"));
926    ui->tool_bapp_download->setEnabled(FALSE);
927  }else if( pbiID.isEmpty() ){ //new installation
928    ui->tool_bapp_download->setText(tr("Install Now!"));
929    ico = ":icons/app_download.png";
930    ui->tool_bapp_download->setEnabled(TRUE);
931  }else if( !PBI->upgradeAvailable(pbiID).isEmpty() ){ //Update available
932    ui->tool_bapp_download->setText(tr("Update"));
933    ico = ":icons/app_upgrade.png";
934    ui->tool_bapp_download->setEnabled(TRUE);
935  }else if(!info[1].isEmpty()){  //Downgrade available
936    ui->tool_bapp_download->setText(tr("Downgrade"));
937    ico = ":icons/app_downgrade.png";
938    ui->tool_bapp_download->setEnabled(TRUE);
939  }else{ //already installed (no downgrade available)
940    ui->tool_bapp_download->setText(tr("Installed"));
941    ui->tool_bapp_download->setIcon(QIcon(":icons/dialog-ok.png"));
942    ui->tool_bapp_download->setEnabled(FALSE);
943  }
944  //Now set the icon appropriately if it requires root permissions
945  if(!ico.isEmpty()){
946    if(info[2]=="true"){ //requires root permissions to install
947      ico.replace(".png","-root.png");
948    }
949    ui->tool_bapp_download->setIcon(QIcon(ico));
950  }
951  ui->tool_bapp_download->setWhatsThis(cApp); //set for slot
952}
953
954void MainUI::slotGoToSearch(){
955  searchTimer->stop(); //just in case "return" was pressed to start the search
956  QString search = ui->line_browse_searchbar->text();
957  if(search.isEmpty()){ return; }
958  PBI->searchTerm = search;
959  QTimer::singleShot(1,PBI,SLOT(startAppSearch()));
960  ui->label_bsearch_info->setText( tr("Searching the application database. Please Wait....") );
961    ui->label_bsearch_info->setVisible(TRUE);
962    ui->group_bsearch_best->setVisible(FALSE);
963    ui->group_bsearch_other->setVisible(FALSE);
964 
965}
966       
967void MainUI::slotShowSimilarApps(QStringList apps){
968  qDebug() << " - Similar applications:" << apps;
969  if(apps.isEmpty()){ ui->group_bapp_similar->setVisible(FALSE); }
970  else{
971    clearScrollArea(ui->scroll_bapp_similar);
972    QHBoxLayout *layout = new QHBoxLayout;
973    for(int i=0; i<apps.length(); i++){
974      QStringList appdata = PBI->AppInfo(apps[i],QStringList() << "name" << "icon");
975      if(!appdata.isEmpty()){
976        SmallItemWidget *item = new SmallItemWidget(apps[i],appdata[0],appdata[1],"");
977        connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToApp(QString)) );
978        layout->addWidget(item);
979      }
980    }
981    layout->addStretch(); //add a spacer to the end
982    layout->setContentsMargins(1,1,1,1);
983    ui->scroll_bapp_similar->widget()->setLayout(layout);
984    //Make sure that the similar scrollarea is the proper fit vertically (no vertical scrolling)
985    ui->scroll_bapp_similar->setMinimumHeight(ui->scroll_bapp_similar->widget()->minimumSizeHint().height()+ui->scroll_bapp_similar->horizontalScrollBar()->height());
986    //Now make the group visible as appropriate
987    ui->group_bapp_similar->setVisible(TRUE);
988    if(ui->group_bapp_similar->isChecked()){ ui->scroll_bapp_similar->setVisible(TRUE); }
989    else{ ui->scroll_bapp_similar->setVisible(FALSE); }
990    //Now make sure the app page has the proper layout dimensions
991    ui->page_app->updateGeometry();
992  }
993}
994
995void MainUI::slotShowSearchResults(QStringList best, QStringList rest){
996  //Now display the search results
997  if(best.isEmpty()){
998    ui->label_bsearch_info->setText( QString(tr("No Search Results Found for the term: %1")).arg(ui->line_browse_searchbar->text()) );
999    ui->label_bsearch_info->setVisible(TRUE);
1000    ui->group_bsearch_best->setVisible(FALSE);
1001    ui->group_bsearch_other->setVisible(FALSE);
1002  }else{
1003    ui->label_bsearch_info->setVisible(FALSE);
1004    //Now fill the "best" section
1005    clearScrollArea(ui->scroll_bsearch_best);
1006    QVBoxLayout *layout = new QVBoxLayout;
1007    QStringList info; info << "name" << "shortdescription" << "icon";
1008    for(int i=0; i<best.length(); i++){
1009      QStringList data = PBI->AppInfo(best[i],info);
1010      if(!data.isEmpty()){
1011        //Trim the description if necessary
1012        if(data[1].size()>100){ data[1] = data[1].left(100).append("..."); }
1013        LargeItemWidget *item = new LargeItemWidget(best[i],data[0],data[1],data[2]);
1014        connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToApp(QString)) );
1015        layout->addWidget(item); 
1016      }
1017    }
1018    layout->addStretch();
1019    ui->scroll_bsearch_best->widget()->setLayout(layout);
1020    ui->group_bsearch_best->setVisible(TRUE);
1021   
1022    //Now fill the other results
1023    if(rest.isEmpty()){ ui->group_bsearch_other->setVisible(FALSE); }
1024    else{
1025      ui->group_bsearch_other->setVisible(TRUE);
1026      clearScrollArea(ui->scroll_bsearch_other);
1027      QVBoxLayout *layout2 = new QVBoxLayout;
1028      for(int i=0; i<rest.length(); i++){
1029        QStringList data = PBI->AppInfo(rest[i],info);
1030        if(!data.isEmpty()){
1031          //Trim the description if necessary
1032          if(data[1].size()>100){ data[1] = data[1].left(100).append("..."); }
1033          LargeItemWidget *item = new LargeItemWidget(rest[i],data[0],data[1],data[2]);
1034          connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToApp(QString)) );
1035          layout2->addWidget(item); 
1036        }
1037      }
1038      layout2->addStretch();
1039      ui->scroll_bsearch_other->widget()->setLayout(layout2);
1040    }
1041  }
1042       
1043  //need to make sure the search bar still has keyboard focus (just in case)
1044  ui->tabWidget->setCurrentWidget(ui->tab_browse);
1045  ui->stacked_browser->setCurrentWidget(ui->page_search);
1046 
1047}
1048
1049void MainUI::on_tabWidget_currentChanged(){
1050  if(ui->tabWidget->currentWidget() == ui->tab_browse){
1051    //Refresh the app page if that is the one currently showing
1052    if(ui->stacked_browser->currentWidget() == ui->page_app){ on_tool_browse_app_clicked(); }     
1053  }else{
1054    //Always return to the installed list
1055    ui->stackedWidget->setCurrentWidget(ui->page_install_list);
1056  }
1057}
1058
1059void MainUI::on_tool_browse_home_clicked(){
1060  slotGoToHome();
1061}
1062
1063void MainUI::on_tool_browse_cat_clicked(){
1064  QString cat = Extras::nameToID(ui->tool_browse_cat->text());
1065  if(cCat == cat){ //already loaded - just show it (prevents resetting scroll positions)
1066    ui->stacked_browser->setCurrentWidget(ui->page_cat);
1067  }else{ //load and show the category
1068    slotGoToCategory(cat);
1069  }
1070}
1071
1072void MainUI::on_tool_browse_app_clicked(){
1073  QString appID = Extras::nameToID(ui->tool_browse_app->text());
1074  slotGoToApp(appID);
1075}
1076
1077void MainUI::on_line_browse_searchbar_textChanged(){
1078  //Connect this to a singleshot timer, so the search functionality is only
1079  //  run once after a short wait rather than every time a new character is typed
1080 
1081  //Live search only after 3 characters have been typed
1082  if(ui->line_browse_searchbar->text().length() > 2){
1083    searchTimer->start();
1084  }else{
1085    searchTimer->stop();         
1086  }
1087}
1088
1089void MainUI::on_tool_bapp_download_clicked(){
1090  QString appID = ui->tool_bapp_download->whatsThis();
1091  //Verify the app installation
1092  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)){
1093    return;
1094  }
1095  PBI->installApp(QStringList() << appID);
1096  ui->tool_bapp_download->setEnabled(FALSE); //make sure it cannot be clicked more than once before page refresh
1097  //Now show the Installed tab
1098  //ui->tabWidget->setCurrentWidget(ui->tab_installed);
1099}
1100
1101void MainUI::on_group_br_home_newapps_toggled(bool show){
1102  ui->scroll_br_home_newapps->setVisible(show);
1103}
1104
1105void MainUI::on_group_bapp_similar_toggled(bool show){
1106  ui->scroll_bapp_similar->setVisible(show);
1107}
1108
1109// ================================
1110// ==========  OTHER ==============
1111// ================================
1112void MainUI::clearScrollArea(QScrollArea* area){
1113  QWidget *wgt = area->takeWidget();
1114  delete wgt; //delete the widget and all children
1115  area->setWidget( new QWidget() ); //create a new widget in the scroll area
1116  area->widget()->setContentsMargins(0,0,0,0);
1117}
1118
1119void MainUI::slotDisplayError(QString title,QString message,QStringList log){
1120  QMessageBox *dlg = new QMessageBox(this);
1121    dlg->setWindowTitle(title);
1122    dlg->setText(message);
1123    dlg->setDetailedText(log.join("\n"));
1124  dlg->exec();
1125}
1126
1127void MainUI::slotDisplayStats(){
1128  int avail = PBI->numAvailable;
1129  int installed = PBI->numInstalled;
1130  QString text;
1131  if(avail == -1){
1132    text = QString(tr("Installed: %1")).arg(QString::number(installed)); 
1133  }else{
1134    text = QString(tr("Installed: %1")+"\t"+tr("Available: %2")).arg(QString::number(installed), QString::number(avail)); 
1135  }
1136  //Get the number of installed/available applications and display it
1137  statusLabel->setText(text);   
1138}
Note: See TracBrowser for help on using the repository browser.