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

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

Fix the auto-update refresh problem after it is clicked. It flickers once, but at least it correctly shows the status of the auto-update for the app now.

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