NumeRe v1.1.4
NumeRe: Framework für Numerische Rechnungen
pluginrepodialog.cpp
Go to the documentation of this file.
1/*****************************************************************************
2 NumeRe: Framework fuer Numerische Rechnungen
3 Copyright (C) 2021 Erik Haenel et al.
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17******************************************************************************/
18
19#include "pluginrepodialog.hpp"
20#include "../IconManager.h"
21#include "../globals.hpp"
22#include "../compositions/grouppanel.hpp"
23#include "../../kernel/core/ui/language.hpp"
24#include "../../kernel/core/utils/tools.hpp"
25#include "../../common/datastructures.h"
26#include "../../common/http.h"
27#include "../controls/searchctrl.hpp"
28
29#include <fstream>
30#include <vector>
31#include <algorithm>
32
33
34#define WINDOWWIDTH 1000*g_pixelScale
35#define CTRLWIDTH WINDOWWIDTH-50*g_pixelScale
36#define WINDOWHEIGHT 600*g_pixelScale
37
38
39
46{
47 private:
48 wxcode::wxTreeListCtrl* m_associatedCtrl;
49
50 protected:
51 // Interaction functions with the wxTreeCtrl
52 virtual bool selectItem(const wxString& value) override;
53 wxTreeItemId findItem(const wxString& value, wxTreeItemId node);
54 virtual wxArrayString getCandidates(const wxString& enteredText) override;
55 wxArrayString getChildCandidates(const wxString& enteredText, wxTreeItemId node);
56
57 public:
58 PackageListSearchCtrl(wxWindow* parent, wxWindowID id, const wxString& hint = wxEmptyString, const wxString& calltip = wxEmptyString, wxcode::wxTreeListCtrl* associatedCtrl = nullptr) : SearchCtrl(parent, id, wxEmptyString), m_associatedCtrl(associatedCtrl)
59 {
60 // Provide a neat hint to the user, what he
61 // may expect from this control
62 SetHint(hint);
63 wxArrayInt sizes;
64 sizes.Add(450, 1);
65 sizes.Add(200, 1);
66 sizes.Add(CTRLWIDTH-650, 1);
67
68 popUp->SetCallTips(calltip);
69 popUp->SetColSizes(sizes);
70 }
71
72};
73
74
84bool PackageListSearchCtrl::selectItem(const wxString& value)
85{
86 // Ensure that a tree as associated
88 {
89 // Get the root node and the first child
90 wxTreeItemIdValue cookie;
91 wxTreeItemId root = m_associatedCtrl->GetRootItem();
92 wxTreeItemId child = m_associatedCtrl->GetFirstChild(root, cookie);
93
94 // If the child exists, try to find the passed string
95 // in the labels of all childs
96 if (child.IsOk())
97 {
98 wxTreeItemId match = findItem(value[0] == ' ' ? value.substr(1).Lower() : value.Lower(), child);
99
100 // If a label was found, ensure it is visible
101 // and select it
102 if (match.IsOk())
103 {
104 m_associatedCtrl->EnsureVisible(match);
105 m_associatedCtrl->SelectItem(match);
106
107 return true;
108 }
109 }
110 }
111
112 return false;
113}
114
115
126wxTreeItemId PackageListSearchCtrl::findItem(const wxString& value, wxTreeItemId node)
127{
128 // Go through all siblings
129 do
130 {
131 // Return the current node, if it
132 // corresponds to the passed string
133 if (m_associatedCtrl->GetItemText(node).Lower() == value)
134 return node;
135
136 // Search the first child
137 wxTreeItemIdValue cookie;
138 wxTreeItemId child = m_associatedCtrl->GetFirstChild(node, cookie);
139
140 // If the child exists, try to find the
141 // passed string in its or its siblings
142 // labels
143 if (child.IsOk())
144 {
145 wxTreeItemId match = findItem(value, child);
146
147 // Return the id, if it exists
148 if (match.IsOk())
149 return match;
150 }
151 }
152 while ((node = m_associatedCtrl->GetNextSibling(node)).IsOk());
153
154 // Return an invalid tree item id, if
155 // nothing had been found
156 return wxTreeItemId();
157}
158
159
169wxArrayString PackageListSearchCtrl::getCandidates(const wxString& enteredText)
170{
171 // Ensure that a tree control was associated
173 {
174 // Find root node and its child
175 wxTreeItemIdValue cookie;
176 wxTreeItemId root = m_associatedCtrl->GetRootItem();
177 wxTreeItemId child = m_associatedCtrl->GetFirstChild(root, cookie);
178
179 // If the child exists, get all labels of its
180 // siblings and their childs, which are candidates
181 // for the passed string
182 if (child.IsOk())
183 return getChildCandidates(enteredText.Lower(), child);
184 }
185
186 // Return an empty string otherwise
187 return wxArrayString();
188}
189
190
203wxArrayString PackageListSearchCtrl::getChildCandidates(const wxString& enteredText, wxTreeItemId node)
204{
205 wxArrayString stringArray;
206 wxArrayString enteredWords = wxStringTokenize(enteredText);
207
208 // Go through all siblings
209 do
210 {
211 size_t nCount = 0;
212 // Append the current label, if it contains the
213 // searched string
214 for (size_t i = 0; i < enteredWords.size(); i++)
215 {
216 if ((m_associatedCtrl->GetItemBold(node) && m_associatedCtrl->GetItemText(node).Lower().find(enteredWords[i]) != std::string::npos)
217 || m_associatedCtrl->GetItemText(node, 1).Lower().find(enteredWords[i]) != std::string::npos
218 || m_associatedCtrl->GetItemText(node, 2).Lower().find(enteredWords[i]) != std::string::npos)
219 nCount++;
220 }
221
222 wxString match;
223
224 // Only add the headline if we had found at least
225 // half of the number of keywords
226 // (otherwise the list will get longer and longer)
227 if (nCount && nCount >= std::rint(2*enteredWords.size() / 3.0))
228 match = m_associatedCtrl->GetItemText(node) + "~" + m_associatedCtrl->GetItemText(node, 1) + "~" + m_associatedCtrl->GetItemText(node, 2);
229
230 // Find the first child of the current node
231 wxTreeItemIdValue cookie;
232 wxTreeItemId child = m_associatedCtrl->GetFirstChild(node, cookie);
233
234 // If the child exists, find the candidates in
235 // its siblings and childs
236 if (child.IsOk())
237 {
238 wxArrayString childArray = getChildCandidates(enteredText, child);
239
240 // If the child array reports a size
241 // we'll add the parent line here
242 if (childArray.size())
243 match = m_associatedCtrl->GetItemText(node) + "~" + m_associatedCtrl->GetItemText(node, 1) + "~" + m_associatedCtrl->GetItemText(node, 2);
244 }
245
246 if (match.length())
247 stringArray.Add(match);
248 }
249 while ((node = m_associatedCtrl->GetNextSibling(node)).IsOk());
250
251 return stringArray;
252}
253
254
255
256
257
258
259
260#define REPO_LOCATION "http://svn.code.sf.net/p/numere/plugins/repository/"
261#define REPO_URL "Repository URL"
262#define DEPENDENCIES "Dependencies"
263
264#define PACKAGCOLUMN 0
265#define REPOCOLUMN 1
266#define INSTALLEDCOLUMN 2
267#define CHANGELOGLENGTH 750
268
269#define INSTALLEDCOLOUR wxColour(220,255,220)
270#define UPDATECOLOUR wxColour(220,220,255)
271#define NEWERCOLOUR wxColour(255,255,160)
272#define OUTDATEDVERSIONCOLOUR wxColour(255,220,220)
273#define LOCALCOLOUR NEWERCOLOUR
274//#define LOCALCOLOUR wxColour(160,255,160)
275
276extern Language _guilang;
277
278BEGIN_EVENT_TABLE(PackageRepoBrowser, ViewerFrame)
281 EVT_TREE_SEL_CHANGED(-1, PackageRepoBrowser::OnItemSelect)
285
286
287
296static std::string getTagValue(const std::string& sTaggedString, const std::string& sTag)
297{
298 int nTag = findParameter(sTaggedString, sTag, '=');
299
300 if (nTag)
301 return getArgAtPos(sTaggedString, nTag+sTag.length());
302
303 return "";
304}
305
306
317{
318 SetSize(WINDOWWIDTH, WINDOWHEIGHT);
319 m_terminal = terminal;
320 m_icons = icons;
322
323 GroupPanel* panel = new GroupPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxBORDER_STATIC);
324
325 m_listCtrl = panel->CreateWxcTreeListCtrl(panel, panel->getMainSizer(), wxTR_TWIST_BUTTONS | wxTR_FULL_ROW_HIGHLIGHT | wxTR_EXTENDED | wxTR_HIDE_ROOT);
326 m_listCtrl->SetImageList(m_icons->GetImageList());
327
328 m_listCtrl->AddColumn("Packages", 300);
329 m_listCtrl->AddColumn("Repository", 500);
330 m_listCtrl->AddColumn("Installed", 145);
331
332 PackageListSearchCtrl* treesearch = new PackageListSearchCtrl(panel, wxID_ANY, "Search packages ...", wxEmptyString, m_listCtrl);
333 panel->getMainSizer()->Prepend(treesearch, 0, wxALL | wxEXPAND, 5);
334
335 m_statusText = panel->AddStaticText(panel, panel->getMainSizer(), "", wxID_ANY, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
336 m_progress = panel->CreateGauge(panel, panel->getMainSizer(), wxGA_HORIZONTAL);
337 m_filesLoaded = false;
338
339 wxBoxSizer* hsizer = panel->createGroup(wxHORIZONTAL, panel->getMainSizer());
340
341 m_installButton = panel->CreateButton(panel, hsizer, "Install/Update", ID_REPODLG_INSTALL);
342 m_uninstallButton = panel->CreateButton(panel, hsizer, "Uninstall", ID_REPODLG_UNINSTALL);
343
344 m_installButton->Disable();
345 m_uninstallButton->Disable();
346
348 PostSizeEvent();
349}
350
351
361{
362 const std::vector<Package>& vInstalled = m_terminal->getInstalledPackages();
363 std::vector<bool> vRemotePackage(vInstalled.size(), false);
364
365 wxTreeItemIdValue cookie;
366 wxTreeItemId item = m_listCtrl->GetFirstChild(m_listCtrl->GetRootItem(), cookie);
367 size_t nNumeReVersion = versionToInt(sVersion);
368
369 while (item.IsOk())
370 {
371 m_listCtrl->SetItemBackgroundColour(item, *wxWHITE);
372 m_listCtrl->SetItemText(item, INSTALLEDCOLUMN, "");
373
374 size_t nRequiredVersion = 0u;
375 std::string sReqVersion = getEntry(item, "Required NumeRe version");
376
377 if (sReqVersion.length())
378 nRequiredVersion = versionToInt(sReqVersion);
379
380 // Ensure that the current NumeRe version is sufficient to handle
381 // this package
382 if (nRequiredVersion > nNumeReVersion)
383 {
384 m_listCtrl->SetItemBackgroundColour(item, OUTDATEDVERSIONCOLOUR);
385
386 if (m_listCtrl->GetItemText(item, REPOCOLUMN).find("(Requires newer NumeRe version)") == std::string::npos)
387 m_listCtrl->SetItemText(item, REPOCOLUMN, m_listCtrl->GetItemText(item, REPOCOLUMN) + " (Requires newer NumeRe version)");
388
389 item = m_listCtrl->GetNextSibling(item);
390 continue;
391 }
392
393 // Find already installed versions
394 for (size_t i = 0; i < vInstalled.size(); i++)
395 {
396 if (vInstalled[i].getName() == m_listCtrl->GetItemText(item))
397 {
398 vRemotePackage[i] = true;
399
400 // If this is a re-run of this function, we already have the locals
401 // in place, so that means, we have to consider the case that this
402 // package has not remote information
403 if (m_listCtrl->GetItemText(item, REPOCOLUMN) == "---")
404 {
405 m_listCtrl->SetItemBackgroundColour(item, LOCALCOLOUR);
406 m_listCtrl->SetItemText(item, INSTALLEDCOLUMN, "v" + vInstalled[i].sVersion + " (Local)");
407 continue;
408 }
409
410 size_t nRepoVersion = versionToInt(m_listCtrl->GetItemText(item, REPOCOLUMN).ToStdString());
411 size_t nInstalledVersion = versionToInt(vInstalled[i].sVersion);
412
413 // Colourize the line backgrounds correspondingly,
414 // if a match had been found
415 if (nRepoVersion > nInstalledVersion)
416 {
417 m_listCtrl->SetItemBackgroundColour(item, UPDATECOLOUR);
418 m_listCtrl->SetItemText(item, INSTALLEDCOLUMN, "v" + vInstalled[i].sVersion + " (Update)");
419 }
420 else if (nRepoVersion < nInstalledVersion) // the installed version is newer
421 {
422 m_listCtrl->SetItemBackgroundColour(item, NEWERCOLOUR);
423 m_listCtrl->SetItemText(item, INSTALLEDCOLUMN, "v" + vInstalled[i].sVersion + " (Newer)");
424 }
425 else // both are equal
426 {
427 m_listCtrl->SetItemBackgroundColour(item, INSTALLEDCOLOUR);
428 m_listCtrl->SetItemText(item, INSTALLEDCOLUMN, "v" + vInstalled[i].sVersion);
429 }
430
431 break;
432 }
433 }
434
435 item = m_listCtrl->GetNextSibling(item);
436 }
437
438 // Delete all obsolete and local entries
439 item = m_listCtrl->GetFirstChild(m_listCtrl->GetRootItem(), cookie);
440
441 while (item.IsOk())
442 {
443 if (m_listCtrl->GetItemBackgroundColour(item) == *wxWHITE && m_listCtrl->GetItemText(item, REPOCOLUMN) == "---")
444 {
445 wxTreeItemId itemToDelete = item;
446 item = m_listCtrl->GetNextSibling(item);
447 m_listCtrl->Delete(itemToDelete);
448 }
449 else
450 item = m_listCtrl->GetNextSibling(item);
451 }
452
453 // Add all local installed packages
454 for (size_t i = 0; i < vRemotePackage.size(); i++)
455 {
456 if (!vRemotePackage[i])
457 {
458 wxTreeItemId currPackage = m_listCtrl->AppendItem(m_listCtrl->GetRootItem(), vInstalled[i].getName());
459 m_listCtrl->SetItemText(currPackage, INSTALLEDCOLUMN, "v" + vInstalled[i].sVersion + " (Local)");
460 m_listCtrl->SetItemText(currPackage, REPOCOLUMN, "---");
461 m_listCtrl->SetItemBold(currPackage, true);
462 m_listCtrl->SetItemBackgroundColour(currPackage, LOCALCOLOUR);
463 m_listCtrl->SetItemImage(currPackage, PACKAGCOLUMN, m_icons->GetIconIndex("nscr"));
464
465 wxTreeItemId currPackageInfo = m_listCtrl->AppendItem(currPackage, "Author");
466 m_listCtrl->SetItemText(currPackageInfo, REPOCOLUMN, vInstalled[i].getAuthor());
467
468 currPackageInfo = m_listCtrl->AppendItem(currPackage, "Type");
469 m_listCtrl->SetItemText(currPackageInfo, REPOCOLUMN, vInstalled[i].sType.find("PLUGIN") != std::string::npos ? "Plugin" : "Package");
470
471 currPackageInfo = m_listCtrl->AppendItem(currPackage, "Description");
472 std::string sDesc = vInstalled[i].getDescription();
473 replaceAll(sDesc, "\\\"", "\"");
474 replaceAll(sDesc, "\\n", "\n");
475
476 if (!sDesc.length() || sDesc == "Description")
477 sDesc = "[No description. Please provide a description using the \"desc=DESC\" install info field.]";
478
479 m_listCtrl->SetItemText(currPackageInfo, REPOCOLUMN, sDesc);
480 m_listCtrl->SetItemToolTip(currPackageInfo, sDesc);
481 m_listCtrl->SetItemToolTip(currPackage, sDesc);
482
483 if (sDesc.front() == '[')
484 m_listCtrl->SetItemTextColour(currPackageInfo, *wxRED);
485
486 if (!vInstalled[i].getKeyWords().length() || vInstalled[i].getKeyWords() != "NONE")
487 {
488 currPackageInfo = m_listCtrl->AppendItem(currPackage, "Keywords");
489 m_listCtrl->SetItemText(currPackageInfo, REPOCOLUMN, vInstalled[i].getKeyWords());
490 m_listCtrl->SetItemToolTip(currPackageInfo, vInstalled[i].getKeyWords());
491 }
492
493 if (!vInstalled[i].getChangesLog().length() || vInstalled[i].getChangesLog() != "NONE")
494 {
495 std::string sChangesLog = vInstalled[i].getChangesLog();
496 replaceAll(sChangesLog, "\\\"", "\"");
497 replaceAll(sChangesLog, "\\n", "\n");
498 currPackageInfo = m_listCtrl->AppendItem(currPackage, "Changelog");
499 m_listCtrl->SetItemText(currPackageInfo, REPOCOLUMN, sChangesLog);
500
501 if (sChangesLog.length() > CHANGELOGLENGTH)
502 sChangesLog = sChangesLog.substr(0, CHANGELOGLENGTH) + "[...]";
503
504 m_listCtrl->SetItemToolTip(currPackageInfo, sChangesLog);
505 }
506
507 std::string sLicense = vInstalled[i].getLicense();
508
509 currPackageInfo = m_listCtrl->AppendItem(currPackage, "License");
510
511 if (sLicense.length() && sLicense != "???")
512 m_listCtrl->SetItemText(currPackageInfo, REPOCOLUMN, sLicense);
513 else
514 {
515 m_listCtrl->SetItemText(currPackageInfo, REPOCOLUMN, "[License unknown. Please provide a license using the \"license=LICENSE\" install info field.]");
516 m_listCtrl->SetItemTextColour(currPackageInfo, *wxRED);
517 }
518 }
519 }
520
521 // Sort the packages to be alphabetically ordered
522 m_listCtrl->SortChildren(m_listCtrl->GetRootItem());
523
524 if (m_progress->GetValue() < 1)
525 {
526 m_statusText->SetLabel("Status: Process finished.");
527 m_progress->SetValue(m_progress->GetRange());
528 }
529}
530
531
541{
542 if (!GetThread() || !GetThread()->IsRunning())
543 {
544 m_task = task;
545
546 if (CreateThread(wxTHREAD_DETACHED) != wxTHREAD_NO_ERROR)
547 return;
548
549 if (GetThread()->Run() != wxTHREAD_NO_ERROR)
550 return;
551 }
552}
553
554
561wxThread::ExitCode PackageRepoBrowser::Entry()
562{
563 try
564 {
565 if (m_task == TASK_LOADREPO)
566 {
567 m_statusText->SetLabel("Status: Fetching Package list ...");
568 m_progress->Pulse();
569 m_listCtrl->AddRoot("ROOT");
570 std::vector<std::string> vRepoContents = getRepoList(REPO_LOCATION);
571
572 m_statusText->SetLabel("Status: Reading Package information ...");
573 m_progress->SetRange(vRepoContents.size());
574 m_progress->SetValue(1);
575
576 for (size_t i = 0; i < vRepoContents.size(); i++)
577 {
578 // Test, whether the user wants to close the window
579 if (GetThread()->TestDestroy())
580 {
582 return (wxThread::ExitCode)0;
583 }
584
585 populatePackageList(vRepoContents[i]);
586 m_progress->SetValue(i+1);
587 }
588
589 m_statusText->SetLabel("Status: Detecting installations ...");
591 m_statusText->SetLabel("Status: Package database successfully loaded.");
592 }
593 else if (m_task == TASK_LOADFILES)
594 {
595 m_statusText->SetLabel("Status: Retrieving packages ...");
596 m_progress->SetRange(m_vUrls.size());
597 m_progress->SetValue(1);
598
599 bool success = true;
600
601 for (size_t i = 0; i < m_vUrls.size(); i++)
602 {
603 // Test, wether the user wants to close the window
604 if (GetThread()->TestDestroy())
605 {
607 return (wxThread::ExitCode)0;
608 }
609
610 success = success && getFileFromRepo(m_vUrls[i]);
611 m_progress->SetValue(i+1);
612 }
613
614 m_filesLoaded = success;
615
616 if (success)
617 m_statusText->SetLabel("Status: Packages successfully downloaded. Waiting for completion ... (You might need to accept licences in terminal)");
618 else
619 m_statusText->SetLabel("Status: One or more packages could not be downloaded.");
620 }
621 }
622 catch (url::Error& e)
623 {
624 m_statusText->SetLabel("HTTP Error: " + std::string(e.what()));
625 m_progress->SetRange(100);
626 m_progress->SetValue(100);
627 }
628
630
631 wxQueueEvent(GetEventHandler(), new wxThreadEvent());
632 return (wxThread::ExitCode)0;
633}
634
635
645void PackageRepoBrowser::OnThreadUpdate(wxThreadEvent& event)
646{
647 if (m_filesLoaded && m_fileNameToInstall.length())
648 {
649 m_filesLoaded = false;
650
651 wxTreeItemIdValue cookie;
652 wxTreeItemId item = m_listCtrl->GetFirstChild(m_listCtrl->GetRootItem(), cookie);
653
654 // Mark downloaded files as installing
655 while (item.IsOk())
656 {
657 if (m_listCtrl->GetItemText(item, INSTALLEDCOLUMN) == "Downloading ...")
658 m_listCtrl->SetItemText(item, INSTALLEDCOLUMN, "Installing ...");
659
660 item = m_listCtrl->GetNextSibling(item);
661 }
662
663 m_progress->SetValue(0);
664 m_progress->Pulse();
665
666 m_terminal->pass_command("install \"packages/" + m_fileNameToInstall + "\"", false);
667 }
668}
669
670
679void PackageRepoBrowser::OnClose(wxCloseEvent& event)
680{
681 if (m_task != TASK_NONE && GetThread() && GetThread()->IsRunning())
682 {
683 if (GetThread()->Delete() != wxTHREAD_NO_ERROR && event.CanVeto())
684 event.Veto();
685 }
686
687 Destroy();
688}
689
690
701std::vector<std::string> PackageRepoBrowser::getRepoList(const std::string& sRepoUrl)
702{
703 std::vector<std::string> vRepoContents;
704 std::string sRepoContents = url::get(sRepoUrl + createSalt());
705 sRepoContents = sRepoContents.substr(sRepoContents.find("<ul>")+4);
706 sRepoContents.erase(sRepoContents.find("</ul>"));
707
708 // Extract each <li></li> pairs content
709 while (sRepoContents.length() && sRepoContents.find("</li>") != std::string::npos)
710 {
711 std::string sCurrentPackage = sRepoContents.substr(0, sRepoContents.find("</li>"));
712 sRepoContents.erase(0, sRepoContents.find("</li>")+5);
713
714 if (sCurrentPackage.find("href=\"") == std::string::npos)
715 break;
716
717 // Get the address
718 sCurrentPackage = sCurrentPackage.substr(sCurrentPackage.find("href=\"") + 6);
719 sCurrentPackage.erase(sCurrentPackage.find('"'));
720
721 // Get the resolved content
722 vRepoContents.push_back(sRepoUrl + sCurrentPackage);
723 }
724
725 return vRepoContents;
726}
727
728
740void PackageRepoBrowser::populatePackageList(const std::string& sUrl)
741{
742 // We do not want to follow parent directory
743 // references
744 if (sUrl.find("/../") != std::string::npos)
745 return;
746
747 // If the current URL is not a file, it might be a folder,
748 // therefore we trigger a recursion here
749 if (sUrl.back() == '/')
750 {
751 std::vector<std::string> vRepoContents = getRepoList(sUrl);
752
753 for (const std::string& _url : vRepoContents)
754 {
756 }
757
758 return;
759 }
760
761 // We only accept NSCR files at the moment
762 if (sUrl.find(".nscr") == std::string::npos)
763 return;
764
765 std::string sCurrentPackage = url::get(sUrl + createSalt());
766
767 // Get the information
768 std::string sInfo = sCurrentPackage.substr(sCurrentPackage.find("<info>"), sCurrentPackage.find("<endinfo>") - sCurrentPackage.find("<info>"));
769 replaceAll(sInfo, "\t", " ");
770 replaceAll(sInfo, "\n", " ");
771 replaceAll(sInfo, "\r", " ");
772
773 // Fill the package list
774 std::string sPackageName = getTagValue(sInfo, "name");
775 wxTreeItemId currPackage = m_listCtrl->AppendItem(m_listCtrl->GetRootItem(), sPackageName);
776 m_listCtrl->SetItemText(currPackage, REPOCOLUMN, "v" + getTagValue(sInfo, "version"));
777 m_listCtrl->SetItemBold(currPackage, true);
778 m_listCtrl->SetItemImage(currPackage, PACKAGCOLUMN, m_icons->GetIconIndex("nscr"));
779
780 wxTreeItemId currPackageInfo = m_listCtrl->AppendItem(currPackage, "Author");
781 m_listCtrl->SetItemText(currPackageInfo, REPOCOLUMN, getTagValue(sInfo, "author"));
782
783 currPackageInfo = m_listCtrl->AppendItem(currPackage, "Type");
784 m_listCtrl->SetItemText(currPackageInfo, REPOCOLUMN, getTagValue(sInfo, "type").find("PLUGIN") != std::string::npos ? "Plugin" : "Package");
785
786 std::string sReqVersion = getTagValue(sInfo, "requireversion");
787
788 if (sReqVersion.length())
789 {
790 currPackageInfo = m_listCtrl->AppendItem(currPackage, "Required NumeRe version");
791 m_listCtrl->SetItemText(currPackageInfo, 1, "v" + sReqVersion);
792 }
793
794 std::string sDeps = getTagValue(sInfo, "requirepackages");
795
796 if (sDeps.length())
797 {
798 currPackageInfo = m_listCtrl->AppendItem(currPackage, DEPENDENCIES);
799 m_listCtrl->SetItemText(currPackageInfo, 1, sDeps);
800 }
801
802 currPackageInfo = m_listCtrl->AppendItem(currPackage, "Description");
803 std::string sDesc = getTagValue(sInfo, "desc");
804
805 if (!sDesc.length())
806 sDesc = getTagValue(sInfo, "plugindesc");
807
808 replaceAll(sDesc, "\\\"", "\"");
809 replaceAll(sDesc, "\\n", "\n");
810
811 if (!sDesc.length())
812 sDesc = "[No description. Please provide a description using the \"desc=DESC\" install info field.]";
813
814 m_listCtrl->SetItemText(currPackageInfo, REPOCOLUMN, sDesc);
815 m_listCtrl->SetItemToolTip(currPackageInfo, sDesc);
816 m_listCtrl->SetItemToolTip(currPackage, sDesc);
817
818 if (sDesc.front() == '[')
819 m_listCtrl->SetItemTextColour(currPackageInfo, *wxRED);
820
821 std::string sKeyWords = getTagValue(sInfo, "keywords");
822
823 if (sKeyWords.length())
824 {
825 currPackageInfo = m_listCtrl->AppendItem(currPackage, "Keywords");
826 m_listCtrl->SetItemText(currPackageInfo, REPOCOLUMN, sKeyWords);
827 m_listCtrl->SetItemToolTip(currPackageInfo, sKeyWords);
828 }
829
830 std::string sChangesLog = getTagValue(sInfo, "changelog");
831
832 if (sChangesLog.length())
833 {
834 replaceAll(sChangesLog, "\\\"", "\"");
835 replaceAll(sChangesLog, "\\n", "\n");
836
837 currPackageInfo = m_listCtrl->AppendItem(currPackage, "Changelog");
838 m_listCtrl->SetItemText(currPackageInfo, REPOCOLUMN, sChangesLog);
839
840 if (sChangesLog.length() > CHANGELOGLENGTH)
841 sChangesLog = sChangesLog.substr(0, CHANGELOGLENGTH) + "[...]";
842
843 m_listCtrl->SetItemToolTip(currPackageInfo, sChangesLog);
844 }
845
846 std::string sLicense = getTagValue(sInfo, "license");
847
848 currPackageInfo = m_listCtrl->AppendItem(currPackage, "License");
849
850 if (sLicense.length())
851 m_listCtrl->SetItemText(currPackageInfo, REPOCOLUMN, sLicense);
852 else
853 {
854 m_listCtrl->SetItemText(currPackageInfo, REPOCOLUMN, "[License unknown. Please provide a license using the \"license=LICENSE\" install info field.]");
855 m_listCtrl->SetItemTextColour(currPackageInfo, *wxRED);
856 }
857
858 currPackageInfo = m_listCtrl->AppendItem(currPackage, REPO_URL);
859 m_listCtrl->SetItemText(currPackageInfo, REPOCOLUMN, sUrl);
860}
861
862
871void PackageRepoBrowser::OnInstall(wxCommandEvent& event)
872{
873 wxTreeItemId item = m_listCtrl->GetSelection();
874
875 if (item.IsOk() && m_listCtrl->HasChildren(item))
876 {
877 m_vUrls.clear();
878 std::vector<std::string> vDeps;
879 m_vUrls.push_back(getUrl(item));
881 m_fileNameToInstall.erase(0, m_fileNameToInstall.rfind('/')+1);
882 m_installButton->Disable();
883 m_listCtrl->SetItemText(item, INSTALLEDCOLUMN, "Downloading ...");
884 m_listCtrl->SetItemBackgroundColour(item, INSTALLEDCOLOUR);
885
886 std::string sDepList = getDependencies(item);
887
888 if (sDepList.length())
889 {
890 resolveDependencies(sDepList, vDeps);
891
892 for (const std::string& sDep : vDeps)
893 {
894 wxTreeItemId dep = findPackage(sDep + ".nscr");
895 m_listCtrl->SetItemText(dep, INSTALLEDCOLUMN, "Downloading ...");
896 m_listCtrl->SetItemBackgroundColour(dep, INSTALLEDCOLOUR);
897
898 if (dep.IsOk())
899 m_vUrls.push_back(getUrl(dep));
900 }
901 }
902
903 m_filesLoaded = false;
905 }
906}
907
908
917void PackageRepoBrowser::OnUninstall(wxCommandEvent& event)
918{
919 wxTreeItemId item = m_listCtrl->GetSelection();
920
921 if (item.IsOk())
922 {
923 wxString sName = m_listCtrl->GetItemText(item);
924 m_terminal->pass_command("uninstall \"" + sName.ToStdString() + "\"", false);
925 m_listCtrl->SetItemText(item, INSTALLEDCOLUMN, "");
926 m_listCtrl->SetItemBackgroundColour(item, *wxWHITE);
927 m_installButton->Enable();
928 m_uninstallButton->Disable();
929 }
930}
931
932
943void PackageRepoBrowser::OnItemSelect(wxTreeEvent& event)
944{
945 wxTreeItemId item = event.GetItem();
946 m_installButton->Disable();
947 m_uninstallButton->Disable();
948
949 if (m_task == TASK_NONE
950 && item.IsOk()
951 && m_listCtrl->HasChildren(item)
952 && m_listCtrl->GetItemBackgroundColour(item) != OUTDATEDVERSIONCOLOUR)
953 {
954 if (isInstallable(item) || isUpdateable(item))
955 m_installButton->Enable();
956
957 if (!isInstallable(item) || isUpdateable(item))
958 m_uninstallButton->Enable();
959 }
960}
961
962
975{
976 return "?" + std::to_string(clock());
977}
978
979
988bool PackageRepoBrowser::isInstallable(const wxTreeItemId& item)
989{
990 return item.IsOk() && m_listCtrl->GetItemBackgroundColour(item) == *wxWHITE;
991}
992
993
1003bool PackageRepoBrowser::isUpdateable(const wxTreeItemId& item)
1004{
1005 return item.IsOk() && m_listCtrl->GetItemBackgroundColour(item) == UPDATECOLOUR;
1006}
1007
1008
1019std::string PackageRepoBrowser::getEntry(const wxTreeItemId& item, const std::string& sIdentifier)
1020{
1021 if (!item.IsOk())
1022 return "";
1023
1024 wxTreeItemIdValue cookie;
1025 wxTreeItemId child = m_listCtrl->GetFirstChild(item, cookie);
1026
1027 while (m_listCtrl->GetItemText(child) != sIdentifier)
1028 {
1029 child = m_listCtrl->GetNextSibling(child);
1030
1031 if (!child.IsOk())
1032 return "";
1033 }
1034
1035 return m_listCtrl->GetItemText(child, REPOCOLUMN).ToStdString();
1036}
1037
1038
1046std::string PackageRepoBrowser::getUrl(const wxTreeItemId& item)
1047{
1048 return getEntry(item, REPO_URL);
1049}
1050
1051
1059std::string PackageRepoBrowser::getDependencies(const wxTreeItemId& item)
1060{
1061 return getEntry(item, DEPENDENCIES);
1062}
1063
1064
1075wxTreeItemId PackageRepoBrowser::findPackage(const std::string& sPackageFileName)
1076{
1077 wxTreeItemIdValue cookie;
1078 wxTreeItemId child = m_listCtrl->GetFirstChild(m_listCtrl->GetRootItem(), cookie);
1079
1080 while (child.IsOk())
1081 {
1082 std::string sPackageUrl = getUrl(child);
1083
1084 if (sPackageUrl.substr(sPackageUrl.rfind('/')) == "/" + sPackageFileName)
1085 return child;
1086
1087 child = m_listCtrl->GetNextSibling(child);
1088 }
1089
1090 return child;
1091}
1092
1093
1107void PackageRepoBrowser::resolveDependencies(std::string sDepList, std::vector<std::string>& vDeps)
1108{
1109 while (sDepList.length())
1110 {
1111 std::string sDep = getNextArgument(sDepList, true);
1112 wxTreeItemId item = findPackage(sDep + ".nscr");
1113
1114 if ((isInstallable(item) || isUpdateable(item)) && std::find(vDeps.begin(), vDeps.end(), sDep) == vDeps.end())
1115 {
1116 vDeps.push_back(sDep);
1118 }
1119 }
1120}
1121
1122
1132bool PackageRepoBrowser::getFileFromRepo(const std::string& sUrl)
1133{
1134 std::string contents = url::get(sUrl);
1135 std::string filename = sUrl;
1136 filename.erase(0, filename.rfind('/')+1);
1137
1138 std::ofstream file((m_scriptPath + "/packages/" + filename).c_str(), std::ios::binary | std::ios::out | std::ios::trunc);
1139
1140 if (file.good())
1141 {
1142 file.write(contents.c_str(), contents.length());
1143 file.close();
1144
1145 return true;
1146 }
1147
1148 return false;
1149}
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
const std::string sVersion
This class simplifies the creation of simple windows and creates a common layout among all windows.
Definition: grouppanel.hpp:188
wxStaticText * AddStaticText(wxWindow *parent, wxSizer *sizer, const wxString &text, int id=wxID_STATIC, int alignment=wxALIGN_CENTER_VERTICAL)
Add some static test to the current sizer and window.
Definition: grouppanel.cpp:140
wxGauge * CreateGauge(wxWindow *parent, wxSizer *sizer, int style, int id=wxID_ANY, int alignment=wxALIGN_CENTER_VERTICAL)
This member function creates the layout for a progress bar.
Definition: grouppanel.cpp:545
wxcode::wxTreeListCtrl * CreateWxcTreeListCtrl(wxWindow *parent, wxSizer *sizer, int nStyle=wxTR_TWIST_BUTTONS|wxTR_FULL_ROW_HIGHLIGHT|wxTR_EXTENDED, wxSize size=wxDefaultSize, int id=wxID_ANY, int alignment=wxALIGN_CENTER_VERTICAL)
This member function creates the layout for a treelist control in the wxCode variant.
Definition: grouppanel.cpp:424
wxBoxSizer * getMainSizer()
Return the pointer to the current main layout sizer.
Definition: grouppanel.cpp:104
wxStaticBoxSizer * createGroup(const wxString &sGroupName, int orient=wxVERTICAL, wxWindow *parent=nullptr, wxSizer *sizer=nullptr, int expand=0)
Member function to create a group (a static box with a label) in the panel.
Definition: grouppanel.cpp:161
wxButton * CreateButton(wxWindow *parent, wxSizer *sizer, const wxString &description, int id=wxID_ANY, int alignment=wxALIGN_CENTER_VERTICAL)
This member function creates the layout for a button.
Definition: grouppanel.cpp:447
int GetIconIndex(wxString iconInfo)
wxImageList * GetImageList()
This class handles the internal language system and returns the language strings of the selected lang...
Definition: language.hpp:38
The terminal class for the GUI. It's a specialisation of the GenericTerminal.
Definition: terminal.hpp:46
const std::vector< Package > & getInstalledPackages()
Returns the installed plugins as a STL vector.
Definition: terminal.cpp:200
void pass_command(const std::string &command, bool isEvent)
Pass the external command to the kernel without printing it to the console.
Definition: terminal.cpp:925
std::vector< std::string > getPathSettings()
Returns the standard paths as a STL vector.
Definition: terminal.cpp:185
This class specializes the generic search control to interact with the package list of the package re...
PackageListSearchCtrl(wxWindow *parent, wxWindowID id, const wxString &hint=wxEmptyString, const wxString &calltip=wxEmptyString, wxcode::wxTreeListCtrl *associatedCtrl=nullptr)
virtual wxArrayString getCandidates(const wxString &enteredText) override
This method returns an array of strings containing possible candidates for the passed search string.
wxcode::wxTreeListCtrl * m_associatedCtrl
wxTreeItemId findItem(const wxString &value, wxTreeItemId node)
This method searches for the tree item, whose label corresponds to the passed string.
virtual bool selectItem(const wxString &value) override
This method searches and selects the item with the passed label in the associated tree.
wxArrayString getChildCandidates(const wxString &enteredText, wxTreeItemId node)
This method returns an array of strings containing possible candiates for the passed search string,...
This class represents a simple browser for the package repository with an install and uninstall capab...
std::string m_fileNameToInstall
std::string getDependencies(const wxTreeItemId &item)
Return the package dependencies.
bool getFileFromRepo(const std::string &sUrl)
Returns the contents of the passed URL as a file and writes it to its counterpart in the <SCRIPTPATH>...
std::string createSalt()
Simple function to make the URL more unique to avoid server caching (which might resolve in a very de...
std::string getUrl(const wxTreeItemId &item)
Return the package URL.
virtual wxThread::ExitCode Entry()
Secondary thread worker function.
wxButton * m_uninstallButton
void resolveDependencies(std::string sDepList, std::vector< std::string > &vDeps)
This member function resolves the dependencies of a selected file using a recursion....
std::string getEntry(const wxTreeItemId &item, const std::string &sIdentifier)
Returns the value of the selected identifier or an empty string, if the identifier cannot be found.
wxcode::wxTreeListCtrl * m_listCtrl
bool isUpdateable(const wxTreeItemId &item)
Returns, whether the current item is an updateable package (already installed but newer version avail...
NumeReTerminal * m_terminal
wxStaticText * m_statusText
bool isInstallable(const wxTreeItemId &item)
Returns, whether the current item is an installable package (not already installed).
void OnClose(wxCloseEvent &event)
OnClose event handler. Will terminate the thread, if it's running.
void StartThread(ThreadTask task)
Start a new thread with the passed task.
void OnInstall(wxCommandEvent &event)
Button event handler linked to the "install/update" button.
std::vector< std::string > getRepoList(const std::string &sRepoUrl)
This member function fetches the repository main page as HTML and extracts the list of item links in ...
void OnUninstall(wxCommandEvent &event)
Button event handler linked to the "uninstall" button.
PackageRepoBrowser(wxWindow *parent, NumeReTerminal *terminal, IconManager *icons)
PackageRepositoryBrowser constructor. Starts the file loading task.
void OnItemSelect(wxTreeEvent &event)
Item select event handler to enable or disable the buttons depending on the state of the package or w...
wxTreeItemId findPackage(const std::string &sPackageFileName)
Find a package entry from its file name. The returned ID must be checked for validness via wxTreeItem...
std::vector< std::string > m_vUrls
void DetectInstalledPackages()
Detect installed packages by loading the list of installed and comparing them to the loaded contents ...
void populatePackageList(const std::string &sUrl)
Gets an URL and retrieve its counterpart from the repository. If the link does not reference a file b...
void OnThreadUpdate(wxThreadEvent &event)
Thread update event handler. Will trigger the actual installation, if the files were loaded successfu...
Implementation of a generic search control based on a combo box.
Definition: searchctrl.hpp:135
SearchCtrlPopup * popUp
Definition: searchctrl.hpp:143
void SetColSizes(const wxArrayInt &sizes)
Change the column sizes. Will create as many columns as fields are in the array.
Definition: searchctrl.hpp:80
void SetCallTips(const wxString &calltip, const wxString &calltiphighlight=wxEmptyString)
Sets the calltips for the popup list.
Definition: searchctrl.hpp:119
This class generalizes a set of basic floating window functionalities like being closable by pressing...
Definition: viewerframe.hpp:31
A class for URL exceptions.
Definition: http.h:32
virtual const char * what() const noexcept
Definition: http.h:38
@ SCRIPTPATH
@ ID_REPODLG_INSTALL
@ ID_REPODLG_UNINSTALL
CONSTCD11 std::enable_if<!std::chrono::treat_as_floating_point< T >::value, T >::type trunc(T t) NOEXCEPT
Definition: date.h:1113
value_type rint(value_type v)
std::string get(const std::string &sUrl, const std::string &sUserName, const std::string &sPassWord)
Get the contents of a URL.
Definition: http.cpp:251
#define OUTDATEDVERSIONCOLOUR
#define WINDOWWIDTH
#define REPO_URL
#define REPO_LOCATION
#define LOCALCOLOUR
Language _guilang
#define REPOCOLUMN
#define UPDATECOLOUR
#define PACKAGCOLUMN
#define NEWERCOLOUR
#define INSTALLEDCOLUMN
static std::string getTagValue(const std::string &sTaggedString, const std::string &sTag)
Static helper function to extract the value of an install info tag.
#define WINDOWHEIGHT
#define INSTALLEDCOLOUR
#define CHANGELOGLENGTH
#define CTRLWIDTH
#define DEPENDENCIES
#define PACKAGE_REPO_BROWSER_TITLE
int findParameter(const std::string &sCmd, const std::string &sParam, const char cFollowing)
This function searches the passed parameter in the passed command string. If something is found,...
Definition: tools.cpp:113
size_t versionToInt(std::string sVersion)
Converts a version string into a multi-digit integer.
std::string getNextArgument(std::string &sArgList, bool bCut)
Definition: tools.cpp:2294
void replaceAll(std::string &sToModify, const char *sToRep, const char *sNewValue, size_t nStart, size_t nEnd)
This function replaces all occurences of the string sToRep in the string sToModify with the new value...
string getArgAtPos(const string &sCmd, unsigned int nPos, int extraction)
Extracts a options value at the selected position and applies automatic parsing, if necessary.
Definition: tools.cpp:1598
END_EVENT_TABLE()