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

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

Clean up the AppCafe? browser home page - now just have a toolbutton to browser categories, and instead put recommended applications on the home page (new defaultrecommendations.txt resource). These recommendations can be automatically updated later if we add that ability for the repo to include an additional file with this information.

  • Property mode set to 100644
File size: 47.8 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{
688    //Currently installing/removing/updating - show last message from process
689    if(!statF.isEmpty()){ ui->label_install_status->setText(statF); }
690    else{ ui->label_install_status->setText(stat); }
691    ui->group_install_appStat->setVisible(TRUE);
692  }
693}
694
695// ==========================
696// ====== BROWSER TAB =======
697// ==========================
698void MainUI::initializeBrowserTab(){
699  ui->tab_browse->setEnabled(FALSE);
700  //Always make sure that the browser starts off on the "home" page
701  ui->stacked_browser->setCurrentWidget(ui->page_home);
702  //With invisible shortcut buttons
703  ui->tool_browse_cat->setVisible(FALSE);
704  ui->tool_browse_app->setVisible(FALSE);
705  //Clear any items left over from the designer form
706  clearScrollArea(ui->scroll_br_home_newapps);
707  clearScrollArea(ui->scroll_br_home_rec);
708  //Search functionality
709  searchTimer = new QTimer();
710    searchTimer->setSingleShot(TRUE);
711    searchTimer->setInterval(300); // 0.3 sec wait before a search
712    connect(searchTimer,SIGNAL(timeout()),this,SLOT(slotGoToSearch()) );
713  connect(ui->tool_browse_search,SIGNAL(clicked()),this,SLOT(slotGoToSearch()) );
714  connect(ui->line_browse_searchbar,SIGNAL(returnPressed()),this,SLOT(slotGoToSearch()) );
715}
716
717// === SLOTS ===
718void MainUI::slotDisableBrowser(bool shownotification){
719  if(shownotification){ qDebug() << "No Repo Available: De-activating the Browser"; }
720  ui->tabWidget->setCurrentWidget(ui->tab_installed);
721  ui->tab_browse->setEnabled(FALSE);
722  slotDisplayStats();
723}
724
725void MainUI::slotEnableBrowser(){
726  qDebug() << "Repo Ready: - generating browser home page";
727  //Now create the browser home page
728  slotUpdateBrowserHome();
729  //And allow the user to go there
730  ui->tab_browse->setEnabled(TRUE);
731  slotDisplayStats();
732}
733
734void MainUI::slotUpdateBrowserHome(){
735  //Load the Categories
736  QStringList cats = PBI->browserCategories();
737    cats.sort();
738    QMenu *catmenu = new QMenu(this);
739    QStringList info; info << "name" << "shortdescription" << "icon";
740    for(int i=0; i<cats.length(); i++){
741      QStringList data = PBI->CatInfo(cats[i],info);
742      if(!data.isEmpty()){
743      QAction *act = new QAction(QIcon(data[2]), data[0], this);
744        act->setToolTip(data[1]);
745        act->setWhatsThis(cats[i]);
746        catmenu->addAction(act);
747      }
748    }
749    connect(catmenu, SIGNAL(triggered(QAction*)), this, SLOT(slotGoToCatClicked(QAction*)) );
750    ui->tool_browse_gotocat->setMenu(catmenu);
751  //Load the Recommendations
752  clearScrollArea(ui->scroll_br_home_rec);
753  QVBoxLayout *reclayout = new QVBoxLayout;
754  QStringList recList = PBI->getRecommendations();
755  //info.clear(); info << "name" << "shortdescription" << "icon";
756  for(int i=0; i<recList.length(); i++){
757    QStringList data = PBI->AppInfo(recList[i],info);
758    if(!data.isEmpty()){
759      LargeItemWidget *item = new LargeItemWidget(recList[i],data[0],data[1],data[2]);
760      connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToApp(QString)) );
761      reclayout->addWidget(item);
762    }
763  }
764  reclayout->addStretch(); //add a spacer to the end
765  ui->scroll_br_home_rec->widget()->setLayout(reclayout);
766  //Load the newest applications
767  clearScrollArea(ui->scroll_br_home_newapps);
768  QHBoxLayout *newapplayout = new QHBoxLayout;
769  QStringList newapps = PBI->getRecentApps();
770  for(int i=0; i<newapps.length(); i++){
771    QStringList appdata = PBI->AppInfo(newapps[i],QStringList() << "name" << "icon" << "latestversion");
772    if(!appdata.isEmpty()){
773      SmallItemWidget *item = new SmallItemWidget(newapps[i],appdata[0],appdata[1],appdata[2]);
774      connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToApp(QString)) );
775      newapplayout->addWidget(item);
776    }
777  }
778  newapplayout->addStretch(); //add a spacer to the end
779  newapplayout->setContentsMargins(0,0,0,0);
780  newapplayout->setSpacing(0);
781  ui->scroll_br_home_newapps->widget()->setLayout(newapplayout);
782  //Make sure that the newapps scrollarea is the proper fit vertically (no vertical scrolling)
783  ui->scroll_br_home_newapps->setMinimumHeight(ui->scroll_br_home_newapps->widget()->minimumSizeHint().height());
784 
785  //Make sure the new apps area is invisible if no items available
786  if(newapps.isEmpty()){ ui->group_br_home_newapps->setVisible(FALSE); }
787  else{ ui->group_br_home_newapps->setVisible(TRUE); }
788  //make sure the home page is visible in the browser (slotGoToHome without changing tabs)
789  ui->stacked_browser->setCurrentWidget(ui->page_home); 
790  //Make sure the shortcut buttons are disabled
791  ui->tool_browse_cat->setVisible(FALSE);
792  ui->tool_browse_app->setVisible(FALSE); 
793 
794}
795
796void MainUI::slotGoToHome(){
797  ui->tabWidget->setCurrentWidget(ui->tab_browse);
798  ui->stacked_browser->setCurrentWidget(ui->page_home); 
799  //Make sure the shortcut buttons are disabled
800  ui->tool_browse_cat->setVisible(false);
801  ui->tool_browse_app->setVisible(false);
802  ui->tool_browse_gotocat->setVisible(true);
803}
804
805void MainUI::slotGoToCategory(QString cat){
806  qDebug() << "Show Category:" << cat;
807  //Get the apps in this category
808  QStringList apps = PBI->browserApps(cat);
809  if(apps.isEmpty()){
810    qDebug() << "Invalid category:" << cat;
811    return;
812  }
813  clearScrollArea(ui->scroll_br_cat_apps);
814  apps.sort();
815  QVBoxLayout *applayout = new QVBoxLayout;
816  QStringList info; info << "name" << "shortdescription" << "icon";
817  for(int i=0; i<apps.length(); i++){
818    QStringList data = PBI->AppInfo(apps[i],info);
819    if(!data.isEmpty()){
820      //Trim the description if necessary
821      if(data[1].size()>100){ data[1] = data[1].left(100).append("..."); }
822      LargeItemWidget *item = new LargeItemWidget(apps[i],data[0],data[1],data[2]);
823      connect(item,SIGNAL(appClicked(QString)),this,SLOT(slotGoToApp(QString)) );
824      applayout->addWidget(item); 
825    }
826  }
827  applayout->addStretch();
828  ui->scroll_br_cat_apps->widget()->setLayout(applayout);
829  //Now enable/disable the shortcut buttons
830  ui->tool_browse_app->setVisible(false);
831  ui->tool_browse_cat->setVisible(true);
832  ui->tool_browse_gotocat->setVisible(false);
833    QStringList catinfo = PBI->CatInfo(cat,QStringList() << "name" << "icon");
834    ui->tool_browse_cat->setText(catinfo[0]);
835    if(catinfo[1].isEmpty()){ catinfo[1] = defaultIcon; }
836    ui->tool_browse_cat->setIcon(QIcon(catinfo[1]));
837  ui->tabWidget->setCurrentWidget(ui->tab_browse);
838  ui->stacked_browser->setCurrentWidget(ui->page_cat);
839  //Now save that this category is currently displayed
840  cCat = cat;
841}
842
843void MainUI::slotGoToCatClicked(QAction* act){
844  slotGoToCategory(act->whatsThis());   
845}
846
847void MainUI::slotGoToApp(QString appID){
848  qDebug() << "Show App:" << appID;
849  //Get the general application info
850  QStringList data; 
851  data << "name" << "icon" << "author" << "website" << "license" << "type" << "description" \
852       << "category" << "requiresroot" << "latestversion" << "latestarch" <<"latestsize" \
853       << "backupversion" << "backuparch" << "backupsize";
854  data = PBI->AppInfo(appID,data);
855  if(data.isEmpty()){
856    qDebug() << "Invalid App:" << appID;
857    return;
858  }
859  cApp = appID; //save this for later
860  //Start the search for similar apps
861  PBI->searchSimilar = appID;
862  ui->group_bapp_similar->setVisible(FALSE);
863  QTimer::singleShot(0,PBI,SLOT(startSimilarSearch()));
864  //Now Check icon
865  if(data[1].isEmpty()){ data[1] = defaultIcon; }
866  //Now fill the UI with the data
867  ui->label_bapp_name->setText(data[0]);
868  ui->label_bapp_icon->setPixmap(QPixmap(data[1]));
869  if(data[3].isEmpty()){ ui->label_bapp_authorweb->setText(data[2]); }
870  else{ ui->label_bapp_authorweb->setText("<a href="+data[3]+">"+data[2]+"</a>"); }
871  ui->label_bapp_authorweb->setToolTip(data[3]);
872  ui->label_bapp_license->setText(data[4]);
873  ui->label_bapp_type->setText(data[5]);
874  ui->text_bapp_description->setText(data[6]);
875  //Now determine the appropriate version info
876  QString pbiID = PBI->isInstalled(appID); //get pbiID
877  QString cVer;
878  if(!pbiID.isEmpty()){ cVer = PBI->PBIInfo(pbiID,QStringList("version")).join(""); }
879  bool useLatest=FALSE;
880  bool nobackup = data[12].isEmpty();
881  if(cVer.isEmpty()){ useLatest=TRUE; } //not currently installed
882  else if(cVer != data[9]){ useLatest=TRUE;} //not the latest version
883  //Now put the proper version info on the UI
884  if(useLatest || nobackup){
885    ui->label_bapp_version->setText(data[9]);
886    ui->label_bapp_arch->setText(data[10]);
887    if(data[11].isEmpty()){ ui->label_bapp_size->setText(tr("Unknown")); }
888    else{ ui->label_bapp_size->setText( Extras::sizeKToDisplay(data[11]) ); }
889  }else{
890    ui->label_bapp_version->setText(data[12]);
891    ui->label_bapp_arch->setText(data[13]);
892    if(data[14].isEmpty()){ ui->label_bapp_size->setText(tr("Unknown")); }
893    else{ ui->label_bapp_size->setText( Extras::sizeKToDisplay(data[14]) ); }
894  }
895  //Now update the download button appropriately
896  slotUpdateAppDownloadButton();
897
898  //Now enable/disable the shortcut buttons
899  ui->tool_browse_app->setVisible(TRUE);
900    ui->tool_browse_app->setText(data[0]);
901    ui->tool_browse_app->setIcon(QIcon(data[1]));
902  QStringList catinfo = PBI->CatInfo(Extras::nameToID(data[7]),QStringList() << "name" << "icon");
903  if(!catinfo.isEmpty()){
904    ui->tool_browse_gotocat->setVisible(false);
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()/1.2);
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.