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

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

minor cosmetic change to the visility of the category buttons in the AppCafe? Browser. Also show which category is being viewed when browsing a particular category.

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