linuxdcpp-team team mailing list archive
-
linuxdcpp-team team
-
Mailing list archive
-
Message #06790
[Branch ~dcplusplus-team/dcplusplus/trunk] Rev 3270: Package plugins as .dcext files
------------------------------------------------------------
revno: 3270
committer: poy <poy@xxxxxxxxxx>
branch nick: trunk
timestamp: Tue 2013-04-23 00:31:37 +0200
message:
Package plugins as .dcext files
added:
Plugin format (dcext).txt
win32/PluginInfoDlg.cpp
win32/PluginInfoDlg.h
renamed:
Extensions.txt => NMDC extensions.txt
modified:
Compile.txt
changelog.txt
dcpp/Archive.cpp
dcpp/Archive.h
dcpp/PluginManager.cpp
dcpp/PluginManager.h
dcpp/SettingsManager.cpp
dcpp/SettingsManager.h
help/settings_advanced.html
win32/AdvancedPage.cpp
win32/MainWindow.cpp
win32/PluginPage.cpp
win32/PluginPage.h
win32/WinUtil.cpp
win32/WinUtil.h
--
lp:dcplusplus
https://code.launchpad.net/~dcplusplus-team/dcplusplus/trunk
Your team Dcplusplus-team is subscribed to branch lp:dcplusplus.
To unsubscribe from this branch go to https://code.launchpad.net/~dcplusplus-team/dcplusplus/trunk/+edit-subscription
=== modified file 'Compile.txt'
--- Compile.txt 2013-03-27 16:24:06 +0000
+++ Compile.txt 2013-04-22 22:31:37 +0000
@@ -184,3 +184,12 @@
many have done), and if you're lucky it might become more popular than the original =). Please
state explicitly when submitting the patch that you give me copyright over the code if the submission is larger
than trivial.
+
+ f. Developing plugins
+
+ See <https://launchpad.net/dcpp-plugin-sdk-c> and
+ <https://launchpad.net/dcpp-plugin-sdk-cpp> for C and C++ plugin SDKs.
+
+ See dcpp/Plugin* files for implementation details.
+
+ The "Plugin format (dcext).txt" document describes the way plugins are packaged.
=== renamed file 'Extensions.txt' => 'NMDC extensions.txt'
=== added file 'Plugin format (dcext).txt'
--- Plugin format (dcext).txt 1970-01-01 00:00:00 +0000
+++ Plugin format (dcext).txt 2013-04-22 22:31:37 +0000
@@ -0,0 +1,63 @@
+This document describes the way DC plugins are packaged and distributed.
+
+More resources:
+- C SDK: <https://launchpad.net/dcpp-plugin-sdk-c>.
+- C++ SDK: <https://launchpad.net/dcpp-plugin-sdk-cpp>.
+- Implementation details: dcpp/Plugin* files.
+
+Alternative names for a DC plugin: DC extension, DC++ plugin, DC++ extension - stemming from the
+host-agnostic design of the DC plugin API.
+
+A DC plugin is, at the very least, a shared extension (.so Linux file, .dll Windows file) that
+defines a "pluginInit" function and handles basic events from the plugin API (ON_INSTALL, ON_LOAD,
+etc). It can then subscribe to interfaces such as DCHooks to catch more events.
+
+Shared extensions are fine for testing but impractical to distribute and to have users install.
+Therefore, a DC plugin is preferably packaged as a .dcext file.
+
+A .dcext file is an archive. Currently, it is required to be a tar file, either uncompressed or
+compressed with bzip2 or gzip. This may be expanded in the future if needed.
+
+That archive must contain an XML file named "info.xml" at its root, whose contents shall validate
+against the schemas/dcext.xsd schema.
+
+Description of the XML tags:
+
+- "dcext" (compulsory): Root tag.
+
+- "UUID" (compulsory): UUID to uniquely identify this plugin.
+- "Name" (compulsory): Friendly name of the plugin.
+- "Version" (compulsory): Version of the plugin; used for updates.
+- "ApiVersion" (compulsory): Plugin API version the plugin has been built with.
+
+- "Author" (optional): Author of the plugin.
+- "Description" (optional): Short description of the plugin.
+- "Website" (optional): Plugin website.
+
+- "Plugin" (compulsory): Location of the loadable shared extension within the archive. The optional
+ "Arch" attribute of this tag designates the architecture this plugin has been compiled for. It
+ may be one of "x86", "x64"; "x86" is assumed by default in the absence of this attribute.
+ Multiple "Plugin" tags may be provided for different architectures.
+- "Files" (optional): Additional files required by the plugin, each within a "File" tag. "File"
+ tags may contain an "Arch" attribute to specify the architecture the file works on; files are
+ assumed to target every architecture by default.
+
+Example info.xml:
+
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<dcext>
+ <UUID>{f62ed829-def5-4332-a0d7-84d2ec692006}</UUID>
+ <Name>Test plugin</Name>
+ <Version>2.3</Version>
+ <ApiVersion>6</ApiVersion>
+ <Author>Test team</Author>
+ <Description>Plugin to do X</Description>
+ <Website>http://example.com</Website>
+ <Plugin Arch="x86">x86/TestPlugin.so</Plugin>
+ <Plugin Arch="x64">x64/TestPlugin.so</Plugin>
+ <Files>
+ <File>icons/TestPlugin.ico</File>
+ <File>fonts/cool.font</File>
+ <File Arch="x64">FasterHash.so</File>
+ </Files>
+</dcext>
=== modified file 'changelog.txt'
--- changelog.txt 2013-04-18 19:46:26 +0000
+++ changelog.txt 2013-04-22 22:31:37 +0000
@@ -8,6 +8,7 @@
* [L#190964] Handle more connection errors (poy)
* Support city-level GeoIP databases - new params such as %[city] (poy)
* Distribute an x64 version
+* Package plugins as .dcext files (poy)
-- 0.811 2013-03-04 --
* Fix status bar parts when the window is too small (poy)
=== modified file 'dcpp/Archive.cpp'
--- dcpp/Archive.cpp 2013-04-21 17:39:36 +0000
+++ dcpp/Archive.cpp 2013-04-22 22:31:37 +0000
@@ -60,25 +60,26 @@
}
void Archive::extract(const string& path) {
- dcassert(!path.empty() && (*(path.end() - 1) == '/' || *(path.end() - 1) == '\\'));
+ auto isDir = [](const string& path) { return *(path.end() - 1) == '/' || *(path.end() - 1) == '\\'; };
+
+ dcassert(!path.empty() && isDir(path));
::archive_entry* entry;
- while(true) {
- if(check(archive_read_next_header(a, &entry)) == ARCHIVE_EOF) {
- break;
+ while(check(archive_read_next_header(a, &entry)) != ARCHIVE_EOF) {
+
+ string path_out(archive_entry_pathname(entry));
+ if(path_out.empty() || isDir(path_out)) {
+ continue;
}
+ path_out = path + path_out;
- auto path_out = path + archive_entry_pathname(entry);
File::ensureDirectory(path_out);
File f_out(path_out, File::WRITE, File::CREATE | File::TRUNCATE);
const void* buf;
size_t size;
__LA_INT64_T offset;
- while(true) {
- if(check(archive_read_data_block(a, &buf, &size, &offset)) == ARCHIVE_EOF) {
- break;
- }
+ while(check(archive_read_data_block(a, &buf, &size, &offset)) != ARCHIVE_EOF) {
f_out.write(buf, size);
}
}
=== modified file 'dcpp/Archive.h'
--- dcpp/Archive.h 2013-04-21 17:39:36 +0000
+++ dcpp/Archive.h 2013-04-22 22:31:37 +0000
@@ -33,6 +33,7 @@
Archive(const string& path);
~Archive();
+ /** Extract all the files in the archive to the specified directory. Throws on errors. */
void extract(const string& path);
private:
=== modified file 'dcpp/PluginManager.cpp'
--- dcpp/PluginManager.cpp 2013-04-21 17:35:50 +0000
+++ dcpp/PluginManager.cpp 2013-04-22 22:31:37 +0000
@@ -19,9 +19,11 @@
#include "stdinc.h"
#include "PluginManager.h"
+#include "Archive.h"
#include "Client.h"
#include "ClientManager.h"
#include "ConnectionManager.h"
+#include "File.h"
#include "LogManager.h"
#include "QueueManager.h"
#include "SimpleXML.h"
@@ -68,6 +70,109 @@
PluginManager::~PluginManager() {
}
+DcextInfo PluginManager::extract(const string& path) {
+ const auto dir = Util::getTempPath() + "dcext" PATH_SEPARATOR_STR;
+ const auto info_path = dir + "info.xml";
+ File::deleteFile(info_path);
+ Archive(path).extract(dir);
+
+ SimpleXML xml;
+ xml.fromXML(File(info_path, File::READ, File::OPEN).read());
+
+ DcextInfo info;
+
+ if(xml.findChild("dcext")) {
+ xml.stepIn();
+
+ auto parse = [&xml](string tag, string& out) {
+ xml.resetCurrentChild();
+ if(xml.findChild(tag)) {
+ out = xml.getChildData();
+ }
+ };
+
+ auto checkArch = [&xml] {
+ auto arch = xml.getChildAttrib("Arch", "x86");
+#if defined(__x86_64__) || defined(_WIN64)
+ return arch == "x64";
+#elif defined(__i386__) || defined(_M_IX86)
+ return arch == "x86";
+#else
+#error Unknown architecture
+#endif
+ };
+
+ string version;
+ parse("ApiVersion", version);
+ if(Util::toInt(version) < DCAPI_CORE_VER) {
+ throw str(F_("%1% is too old, contact the plugin author for an update") % Util::getFileName(path));
+ }
+
+ parse("UUID", info.uuid);
+ parse("Name", info.name);
+ version.clear(); parse("Version", version); info.version = Util::toDouble(version);
+ parse("Author", info.author);
+ parse("Description", info.description);
+ parse("Website", info.website);
+
+ xml.resetCurrentChild();
+ while(xml.findChild("Plugin")) {
+ if(checkArch()) {
+ info.plugin = xml.getChildData();
+ }
+ }
+
+ xml.resetCurrentChild();
+ if(xml.findChild("Files")) {
+ xml.stepIn();
+
+ while(xml.findChild("File")) {
+ if(checkArch()) {
+ info.files.push_back(xml.getChildData());
+ }
+ }
+
+ xml.stepOut();
+ }
+
+ xml.stepOut();
+ }
+
+ if(info.uuid.empty() || info.name.empty() || info.version == 0 || info.plugin.empty()) {
+ throw str(F_("%1% is not a valid DC extension") % Util::getFileName(path));
+ }
+
+ {
+ Lock l(cs);
+
+ if(isLoaded(info.uuid)) {
+ throw str(F_("%1% is already installed") % Util::getFileName(path));
+ }
+ }
+
+ return info;
+}
+
+void PluginManager::install(const string& name, const string& plugin, const StringList& files) {
+ if(name.empty() || plugin.empty()) {
+ throw Exception();
+ }
+
+ const auto source = Util::getTempPath() + "dcext" PATH_SEPARATOR_STR;
+ const auto target = Util::getPath(Util::PATH_USER_LOCAL) + "Plugins" PATH_SEPARATOR_STR + name + PATH_SEPARATOR_STR;
+ const auto lib = target + Util::getFileName(plugin);
+
+ File::ensureDirectory(lib);
+ File::renameFile(source + plugin, lib);
+
+ for(auto& file: files) {
+ File::ensureDirectory(target + file);
+ File::renameFile(source + file, target + file);
+ }
+
+ loadPlugin(lib, [](const string& err) { throw Exception(err); }, true);
+}
+
void PluginManager::loadPlugins(function<void (const string&)> f) {
TimerManager::getInstance()->addListener(this);
ClientManager::getInstance()->addListener(this);
=== modified file 'dcpp/PluginManager.h'
--- dcpp/PluginManager.h 2013-04-21 17:35:50 +0000
+++ dcpp/PluginManager.h 2013-04-22 22:31:37 +0000
@@ -87,6 +87,18 @@
PluginHandle handle;
};
+/** Information about a dcext-packaged plugin that has just been extracted. */
+struct DcextInfo {
+ string uuid;
+ string name;
+ double version;
+ string author;
+ string description;
+ string website;
+ string plugin;
+ StringList files;
+};
+
class PluginManager : public Singleton<PluginManager>, private TimerManagerListener,
private ClientManagerListener, private QueueManagerListener, private SettingsManagerListener
{
@@ -94,6 +106,10 @@
PluginManager();
~PluginManager();
+ /** Extract a dcext-packaged plugin. Throws on errors. */
+ DcextInfo extract(const string& path);
+ void install(const string& name, const string& plugin, const StringList& files);
+
void loadPlugins(function<void (const string&)> f);
bool loadPlugin(const string& fileName, function<void (const string&)> err, bool install = false);
bool isLoaded(const string& guid);
=== modified file 'dcpp/SettingsManager.cpp'
--- dcpp/SettingsManager.cpp 2013-04-16 16:11:50 +0000
+++ dcpp/SettingsManager.cpp 2013-04-22 22:31:37 +0000
@@ -92,7 +92,7 @@
"AwayCompLock", "AwayTimeStamp", "BoldFinishedDownloads", "BoldFinishedUploads", "BoldFL",
"BoldHub", "BoldPm", "BoldQueue", "BoldSearch", "BoldSystemLog", "ClearSearch",
"CompressTransfers", "ConfirmADLSRemoval", "ConfirmExit", "ConfirmHubClosing",
- "ConfirmHubRemoval", "ConfirmItemRemoval", "ConfirmUserRemoval", "Coral",
+ "ConfirmHubRemoval", "ConfirmItemRemoval", "ConfirmUserRemoval", "Coral", "DcextRegister",
"DontDlAlreadyQueued", "DontDLAlreadyShared", "FavShowJoins", "FilterMessages",
"FinishedDLOnlyFull", "FollowLinks", "GeoCity", "GetUserCountry", "GetUserInfo",
"HubUserCommands", "IgnoreBotPms", "IgnoreHubPms", "OpenNewWindow", "KeepFinishedFiles",
@@ -250,6 +250,7 @@
setDefault(POPUNDER_PM, false);
setDefault(POPUNDER_FILELIST, false);
setDefault(MAGNET_REGISTER, true);
+ setDefault(DCEXT_REGISTER, true);
setDefault(MAGNET_ASK, true);
setDefault(MAGNET_ACTION, MAGNET_AUTO_SEARCH);
setDefault(ADD_FINISHED_INSTANTLY, false);
=== modified file 'dcpp/SettingsManager.h'
--- dcpp/SettingsManager.h 2013-04-16 16:11:50 +0000
+++ dcpp/SettingsManager.h 2013-04-22 22:31:37 +0000
@@ -129,7 +129,7 @@
AWAY_COMP_LOCK, AWAY_TIMESTAMP, BOLD_FINISHED_DOWNLOADS, BOLD_FINISHED_UPLOADS, BOLD_FL,
BOLD_HUB, BOLD_PM, BOLD_QUEUE, BOLD_SEARCH, BOLD_SYSTEM_LOG, CLEAR_SEARCH,
COMPRESS_TRANSFERS, CONFIRM_ADLS_REMOVAL, CONFIRM_EXIT, CONFIRM_HUB_CLOSING,
- CONFIRM_HUB_REMOVAL, CONFIRM_ITEM_REMOVAL, CONFIRM_USER_REMOVAL, CORAL,
+ CONFIRM_HUB_REMOVAL, CONFIRM_ITEM_REMOVAL, CONFIRM_USER_REMOVAL, CORAL, DCEXT_REGISTER,
DONT_DL_ALREADY_QUEUED, DONT_DL_ALREADY_SHARED, FAV_SHOW_JOINS, FILTER_MESSAGES,
FINISHED_DL_ONLY_FULL, FOLLOW_LINKS, GEO_CITY, GET_USER_COUNTRY, GET_USER_INFO,
HUB_USER_COMMANDS, IGNORE_BOT_PMS, IGNORE_HUB_PMS, JOIN_OPEN_NEW_WINDOW, KEEP_FINISHED_FILES,
=== modified file 'help/settings_advanced.html'
--- help/settings_advanced.html 2013-04-16 16:11:50 +0000
+++ help/settings_advanced.html 2013-04-22 22:31:37 +0000
@@ -39,7 +39,14 @@
information appropriate for the Direct Connect network.
Disabling will make DC++ remove the key from
the Windows registry.
- </dd>
+</dd>
+
+<dt>Register with Windows to handle .dcext files</dt>
+<dd cshelp="IDH_SETTINGS_ADVANCED_DCEXT_REGISTER">
+Associate DC++ with the .dcext file extension in Windows. Those files are plugins that can be
+installed into DC++ to extend its functionality.
+</dd>
+
<dt>Don't delete file lists when exiting</dt>
<dd cshelp="IDH_SETTINGS_ADVANCED_KEEP_LISTS">If this option is disabled, DC++ will delete the contents of the
file list directory, where file lists are stored, when
=== modified file 'win32/AdvancedPage.cpp'
--- win32/AdvancedPage.cpp 2013-04-16 16:11:50 +0000
+++ win32/AdvancedPage.cpp 2013-04-22 22:31:37 +0000
@@ -34,6 +34,7 @@
{ SettingsManager::LIST_DUPES, N_("Keep duplicate files in your file list"), IDH_SETTINGS_ADVANCED_LIST_DUPES },
{ SettingsManager::URL_HANDLER, N_("Register with Windows to handle dchub://, adc:// and adcs:// URL links"), IDH_SETTINGS_ADVANCED_URL_HANDLER },
{ SettingsManager::MAGNET_REGISTER, N_("Register with Windows to handle magnet: URI links"), IDH_SETTINGS_ADVANCED_MAGNET_REGISTER },
+ { SettingsManager::DCEXT_REGISTER, N_("Register with Windows to handle .dcext files"), IDH_SETTINGS_ADVANCED_DCEXT_REGISTER },
{ SettingsManager::KEEP_LISTS, N_("Don't delete file lists when exiting"), IDH_SETTINGS_ADVANCED_KEEP_LISTS },
{ SettingsManager::AUTO_KICK, N_("Automatically disconnect users who leave the hub"), IDH_SETTINGS_ADVANCED_AUTO_KICK },
{ SettingsManager::SFV_CHECK, N_("Enable automatic SFV checking"), IDH_SETTINGS_ADVANCED_SFV_CHECK },
=== modified file 'win32/MainWindow.cpp'
--- win32/MainWindow.cpp 2013-04-16 16:11:50 +0000
+++ win32/MainWindow.cpp 2013-04-22 22:31:37 +0000
@@ -53,30 +53,29 @@
#include <dwt/widgets/SplitterContainer.h>
#include <dwt/widgets/ToolBar.h>
-#include "resource.h"
-
+#include "AboutDlg.h"
+#include "ADLSearchFrame.h"
#include "CrashLogger.h"
-#include "ParamDlg.h"
-#include "HashProgressDlg.h"
-#include "SettingsDialog.h"
-#include "TextFrame.h"
-#include "SingleInstance.h"
-#include "AboutDlg.h"
-#include "TransferView.h"
-#include "HubFrame.h"
-#include "PrivateFrame.h"
#include "DirectoryListingFrame.h"
-#include "SearchFrame.h"
-#include "ADLSearchFrame.h"
#include "FavHubsFrame.h"
#include "FinishedDLFrame.h"
#include "FinishedULFrame.h"
+#include "HashProgressDlg.h"
+#include "HubFrame.h"
#include "NotepadFrame.h"
+#include "ParamDlg.h"
+#include "PluginInfoDlg.h"
+#include "PrivateFrame.h"
#include "PublicHubsFrame.h"
#include "QueueFrame.h"
+#include "resource.h"
#include "SearchFrame.h"
+#include "SettingsDialog.h"
+#include "SingleInstance.h"
#include "StatsFrame.h"
#include "SystemFrame.h"
+#include "TextFrame.h"
+#include "TransferView.h"
#include "UsersFrame.h"
#ifdef HAVE_HTMLHELP_H
@@ -1186,6 +1185,7 @@
auto prevSortFavUsersFirst = SETTING(SORT_FAVUSERS_FIRST);
auto prevURLReg = SETTING(URL_HANDLER);
auto prevMagnetReg = SETTING(MAGNET_REGISTER);
+ auto prevDcextReg = SETTING(DCEXT_REGISTER);
auto prevSettingsSave = SETTING(SETTINGS_SAVE_INTERVAL);
if(SettingsDialog(this).run() == IDOK) {
@@ -1261,6 +1261,8 @@
WinUtil::registerHubHandlers();
if(SETTING(MAGNET_REGISTER) != prevMagnetReg)
WinUtil::registerMagnetHandler();
+ if(SETTING(DCEXT_REGISTER) != prevDcextReg)
+ WinUtil::registerDcextHandler();
if(SETTING(SETTINGS_SAVE_INTERVAL) != prevSettingsSave)
setSaveTimer();
@@ -1532,21 +1534,27 @@
}
}
-void MainWindow::parseCommandLine(const tstring& cmdLine)
-{
- string::size_type i;
-
- if( (i = cmdLine.find(_T("dchub://"))) != string::npos ||
- (i = cmdLine.find(_T("adc://"))) != string::npos ||
- (i = cmdLine.find(_T("adcs://"))) != string::npos ||
- (i = cmdLine.find(_T("magnet:?"))) != string::npos )
+void MainWindow::parseCommandLine(const tstring& cmdLine) {
+ // this string may or may not contain the executable's path at the beginning.
+
+ tstring::size_type i;
+
+ if( (i = cmdLine.find(_T("dchub://"))) != tstring::npos ||
+ (i = cmdLine.find(_T("adc://"))) != tstring::npos ||
+ (i = cmdLine.find(_T("adcs://"))) != tstring::npos ||
+ (i = cmdLine.find(_T("magnet:?"))) != tstring::npos )
{
WinUtil::parseLink(cmdLine.substr(i), false);
+
+ } else if((i = cmdLine.find(_T("dcext:"))) != tstring::npos) {
+ auto path = Text::fromT(cmdLine.substr(i + 6));
+ Util::sanitizeUrl(path);
+ PluginInfoDlg(this, path).run();
}
}
LRESULT MainWindow::handleCopyData(LPARAM lParam) {
- parseCommandLine(dwt::Application::instance().getModuleFileName() + _T(" ") + reinterpret_cast<LPCTSTR>(reinterpret_cast<COPYDATASTRUCT*>(lParam)->lpData));
+ parseCommandLine(reinterpret_cast<LPCTSTR>(reinterpret_cast<COPYDATASTRUCT*>(lParam)->lpData));
return TRUE;
}
=== added file 'win32/PluginInfoDlg.cpp'
--- win32/PluginInfoDlg.cpp 1970-01-01 00:00:00 +0000
+++ win32/PluginInfoDlg.cpp 2013-04-22 22:31:37 +0000
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2001-2013 Jacek Sieka, arnetheduck on gmail point com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "stdafx.h"
+#include "PluginInfoDlg.h"
+
+#include <dcpp/PluginManager.h>
+
+#include <dwt/widgets/Grid.h>
+#include <dwt/widgets/Label.h>
+#include <dwt/widgets/Link.h>
+#include <dwt/widgets/MessageBox.h>
+
+#include "WinUtil.h"
+
+using dwt::Grid;
+using dwt::GridInfo;
+using dwt::Label;
+using dwt::Link;
+
+PluginInfoDlg::PluginInfoDlg(dwt::Widget* parent, const string& path) :
+dwt::ModalDialog(parent),
+grid(0)
+{
+ onInitDialog([this, path] { return handleInitDialog(path); });
+}
+
+PluginInfoDlg::~PluginInfoDlg() {
+}
+
+int PluginInfoDlg::run() {
+ create(dwt::Point(400, 300));
+ return show();
+}
+
+bool PluginInfoDlg::handleInitDialog(const string& path) {
+ grid = addChild(Grid::Seed(0, 2));
+ grid->column(1).mode = GridInfo::FILL;
+ grid->setSpacing(6);
+
+ DcextInfo info;
+ try {
+ info = PluginManager::getInstance()->extract(path);
+
+ } catch(const Exception& e) {
+ resize(dwt::Rectangle());
+ auto err = Text::toT(e.getError());
+ callAsync([this, err, path] {
+ error(err, Text::toT(Util::getFileName(path)));
+ endDialog(IDCANCEL);
+ });
+ return true;
+ }
+
+ // similar to PluginPage.cpp
+
+ enum Type { Name, Version, Description, Author, Website };
+
+ auto addInfo = [this](tstring name, const string& value, Type type) {
+ if(type == Description) {
+ grid->addRow(GridInfo(0, GridInfo::FILL, GridInfo::STRETCH));
+ } else {
+ grid->addRow();
+ }
+ grid->addChild(Label::Seed(name));
+ if(type == Website && !value.empty()) {
+ grid->addChild(Link::Seed(Text::toT(value), true));
+ } else {
+ grid->addChild(Label::Seed(value.empty() ?
+ T_("<Information unavailable>") : Text::toT(value)));
+ }
+ };
+
+ addInfo(T_("Name: "), info.name, Name);
+ addInfo(T_("Version: "), Util::toString(info.version), Version);
+ addInfo(T_("Description: "), info.description, Description);
+ addInfo(T_("Author: "), info.author, Author);
+ addInfo(T_("Website: "), info.website, Website);
+
+ {
+ grid->addRow();
+ auto cur = grid->addChild(Grid::Seed(1, 2));
+ grid->setWidget(cur, grid->rowCount() - 1, 0, 1, 2);
+ cur->column(0).mode = GridInfo::FILL;
+ cur->column(0).align = GridInfo::BOTTOM_RIGHT;
+ cur->setSpacing(grid->getSpacing());
+ WinUtil::addDlgButtons(cur,
+ [this, info] { handleOK(info.name, info.plugin, info.files); },
+ [this] { endDialog(IDCANCEL); }).first->setText(T_("Install the plugin"));
+ }
+
+ setText(T_("Adding a plugin"));
+
+ layout();
+ centerWindow();
+
+ return false;
+}
+
+void PluginInfoDlg::handleOK(const string& name, const string& plugin, const StringList& files) {
+ try {
+ PluginManager::getInstance()->install(name, plugin, files);
+ endDialog(IDOK);
+
+ } catch(const Exception& e) {
+ error(Text::toT(e.getError()), Text::toT(name));
+ endDialog(IDCANCEL);
+ }
+}
+
+void PluginInfoDlg::layout() {
+ dwt::Point sz = getClientSize();
+ grid->resize(dwt::Rectangle(3, 3, sz.x - 6, sz.y - 6));
+}
+
+void PluginInfoDlg::error(const tstring& message, const tstring& title) {
+ dwt::MessageBox(this).show(tstring(T_("Cannot install the plugin:")) + _T("\r\n\r\n") + message, title,
+ dwt::MessageBox::BOX_OK, dwt::MessageBox::BOX_ICONSTOP);
+}
=== added file 'win32/PluginInfoDlg.h'
--- win32/PluginInfoDlg.h 1970-01-01 00:00:00 +0000
+++ win32/PluginInfoDlg.h 2013-04-22 22:31:37 +0000
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2001-2013 Jacek Sieka, arnetheduck on gmail point com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef DCPLUSPLUS_WIN32_PLUGININFODLG_H
+#define DCPLUSPLUS_WIN32_PLUGININFODLG_H
+
+#include <dcpp/typedefs.h>
+
+#include <dwt/widgets/ModalDialog.h>
+
+#include "forward.h"
+
+class PluginInfoDlg : public dwt::ModalDialog
+{
+public:
+ PluginInfoDlg(dwt::Widget* parent, const string& path);
+ virtual ~PluginInfoDlg();
+
+ int run();
+
+private:
+ bool handleInitDialog(const string& path);
+ void handleOK(const string& name, const string& plugin, const StringList& files);
+
+ void layout();
+
+ void error(const tstring& message, const tstring& title);
+
+ GridPtr grid;
+};
+
+#endif
=== modified file 'win32/PluginPage.cpp'
--- win32/PluginPage.cpp 2013-03-16 14:57:07 +0000
+++ win32/PluginPage.cpp 2013-04-22 22:31:37 +0000
@@ -21,6 +21,7 @@
#include "HoldRedraw.h"
#include "resource.h"
+#include "PluginInfoDlg.h"
#include "WinUtil.h"
#include <dwt/widgets/Grid.h>
@@ -98,27 +99,22 @@
add = buttons->addChild(Button::Seed(T_("&Add")));
add->onClicked([this] { handleAddPlugin(); });
- add->setImage(WinUtil::buttonIcon(IDI_OK));
add->setHelpId(IDH_SETTINGS_PLUGINS_ADD);
configure = buttons->addChild(Button::Seed(T_("&Configure")));
configure->onClicked([this] { handleConfigurePlugin(); });
- configure->setImage(WinUtil::buttonIcon(IDI_SETTINGS));
configure->setHelpId(IDH_SETTINGS_PLUGINS_CONFIGURE);
- moveUp = buttons->addChild(Button::Seed(T_("Move &up")));
+ moveUp = buttons->addChild(Button::Seed(T_("Move &Up")));
moveUp->onClicked([this] { handleMovePluginUp(); });
- moveUp->setImage(WinUtil::buttonIcon(IDI_UPLOAD));
moveUp->setHelpId(IDH_SETTINGS_PLUGINS_MOVE_UP);
- moveDown = buttons->addChild(Button::Seed(T_("Move &down")));
+ moveDown = buttons->addChild(Button::Seed(T_("Move &Down")));
moveDown->onClicked([this] { handleMovePluginDown(); });
- moveDown->setImage(WinUtil::buttonIcon(IDI_DOWNLOAD));
moveDown->setHelpId(IDH_SETTINGS_PLUGINS_MOVE_DOWN);
remove = buttons->addChild(Button::Seed(T_("&Remove")));
remove->onClicked([this] { handleRemovePlugin(); });
- remove->setImage(WinUtil::buttonIcon(IDI_CANCEL));
remove->setHelpId(IDH_SETTINGS_PLUGINS_REMOVE);
}
}
@@ -203,6 +199,8 @@
infoGrid->column(1).mode = GridInfo::FILL;
infoGrid->setSpacing(pluginInfo->getSpacing());
+ // similar to PluginInfoDlg.cpp
+
enum Type { Name, Version, Description, Author, Website };
auto addInfo = [this, infoGrid](tstring name, const string& value, Type type) {
@@ -230,44 +228,32 @@
}
bool PluginPage::handleContextMenu(dwt::ScreenCoordinate pt) {
- if(plugins->countSelected() > 0) {
- if(pt.x() == -1 && pt.y() == -1) {
- pt = plugins->getContextMenuPos();
- }
- MenuPtr contextMenu = makeMenu();
- contextMenu->open(pt);
- return true;
+ if(pt.x() == -1 && pt.y() == -1) {
+ pt = plugins->getContextMenuPos();
}
- return false;
-}
-
-MenuPtr PluginPage::makeMenu() {
- MenuPtr menu = addChild(WinUtil::Seeds::menu);
-
- menu->setTitle(T_("Plugin options"), WinUtil::menuIcon(IDI_PLUGINS));
- menu->appendItem(T_("Add plugin"), [this] { handleAddPlugin(); }, WinUtil::menuIcon(IDI_OK));
- menu->appendItem(T_("Configure plugin"), [this] { handleConfigurePlugin(); }, WinUtil::menuIcon(IDI_SETTINGS));
- menu->appendSeparator();
- menu->appendItem(T_("Move plugin up"), [this] { handleMovePluginUp(); }, WinUtil::menuIcon(IDI_UPLOAD));
- menu->appendItem(T_("Move plugin down"), [this] { handleMovePluginDown(); }, WinUtil::menuIcon(IDI_DOWNLOAD));
- menu->appendSeparator();
- menu->appendItem(T_("Remove plugin"), [this] { handleRemovePlugin(); }, WinUtil::menuIcon(IDI_CANCEL));
-
- return menu;
+
+ auto menu = addChild(WinUtil::Seeds::menu);
+
+ menu->setTitle(T_("Plugins"), WinUtil::menuIcon(IDI_PLUGINS));
+ menu->appendItem(T_("&Add"), [this] { handleAddPlugin(); });
+ menu->appendItem(T_("&Configure"), [this] { handleConfigurePlugin(); });
+ menu->appendSeparator();
+ menu->appendItem(T_("Move &Up"), [this] { handleMovePluginUp(); });
+ menu->appendItem(T_("Move &Down"), [this] { handleMovePluginDown(); });
+ menu->appendSeparator();
+ menu->appendItem(T_("&Remove"), [this] { handleRemovePlugin(); });
+
+ menu->open(pt);
+ return true;
}
void PluginPage::handleAddPlugin() {
tstring path;
- if(LoadDialog(this).addFilter(T_("DLL files"), _T("*.dll"))
- .setInitialDirectory(Text::toT(Util::getPath(Util::PATH_GLOBAL_CONFIG) + "Plugins")).open(path))
+ if(LoadDialog(this).addFilter(T_("dcext files"), _T("*.dcext")).open(path) &&
+ PluginInfoDlg(this, Text::fromT(path)).run() == IDOK)
{
- auto idx = plugins->size();
- if(PluginManager::getInstance()->loadPlugin(Text::fromT(path), [this, &path](const string& str) {
- dwt::MessageBox(this).show(Text::toT(str), Text::toT(Util::getFileName(Text::fromT(path))),
- dwt::MessageBox::BOX_OK, dwt::MessageBox::BOX_ICONSTOP);
- }, true)) {
- addEntry(idx, PluginManager::getInstance()->getPlugin(idx)->getInfo());
- }
+ auto pos = plugins->size();
+ addEntry(pos, PluginManager::getInstance()->getPlugin(pos)->getInfo());
}
}
@@ -314,6 +300,9 @@
}
void PluginPage::handleRemovePlugin() {
+ if(plugins->countSelected() != 1)
+ return;
+
auto sel = plugins->getSelected();
PluginManager::getInstance()->unloadPlugin(sel);
plugins->erase(sel);
=== modified file 'win32/PluginPage.h'
--- win32/PluginPage.h 2013-01-18 21:28:38 +0000
+++ win32/PluginPage.h 2013-04-22 22:31:37 +0000
@@ -42,7 +42,6 @@
void handleSelectionChanged();
bool handleContextMenu(dwt::ScreenCoordinate pt);
- MenuPtr makeMenu();
void handleAddPlugin();
void handleConfigurePlugin();
=== modified file 'win32/WinUtil.cpp'
--- win32/WinUtil.cpp 2013-04-12 21:10:13 +0000
+++ win32/WinUtil.cpp 2013-04-22 22:31:37 +0000
@@ -114,6 +114,7 @@
MainWindow* WinUtil::mainWindow = 0;
bool WinUtil::urlDcADCRegistered = false;
bool WinUtil::urlMagnetRegistered = false;
+bool WinUtil::dcextRegistered = false;
DWORD WinUtil::helpCookie = 0;
tstring WinUtil::helpPath;
StringList WinUtil::helpTexts;
@@ -231,6 +232,7 @@
registerHubHandlers();
registerMagnetHandler();
+ registerDcextHandler();
initHelpPath();
@@ -1295,7 +1297,13 @@
return RGB(r, g, b);
}
-bool registerHandler_(const tstring& name) {
+namespace {
+
+void regChanged() {
+ ::SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr);
+}
+
+bool registerHandler_(const tstring& name, const tstring& descr, bool url, const tstring& prefix) {
HKEY hk;
TCHAR Buf[512];
Buf[0] = 0;
@@ -1309,7 +1317,7 @@
::RegCloseKey(hk);
}
- tstring app = _T("\"") + dwt::Application::instance().getModuleFileName() + _T("\" \"%1\"");
+ tstring app = _T("\"") + dwt::Application::instance().getModuleFileName() + _T("\" \"") + prefix + _T("%1\"");
if(Util::stricmp(app.c_str(), Buf) == 0) {
// already registered to us
return true;
@@ -1317,16 +1325,21 @@
if(::RegCreateKeyEx(HKEY_CURRENT_USER, (_T("Software\\Classes\\") + name).c_str(),
0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hk, NULL) != ERROR_SUCCESS)
+ {
return false;
+ }
- TCHAR tmp[] = _T("URL:Direct Connect Protocol");
- ::RegSetValueEx(hk, NULL, 0, REG_SZ, (LPBYTE) tmp, sizeof(TCHAR) * (_tcslen(tmp) + 1));
- ::RegSetValueEx(hk, _T("URL Protocol"), 0, REG_SZ, (LPBYTE) _T(""), sizeof(TCHAR));
+ ::RegSetValueEx(hk, NULL, 0, REG_SZ, (LPBYTE) descr.c_str(), sizeof(TCHAR) * (descr.size() + 1));
+ if(url) {
+ ::RegSetValueEx(hk, _T("URL Protocol"), 0, REG_SZ, (LPBYTE) _T(""), sizeof(TCHAR));
+ }
::RegCloseKey(hk);
if(::RegCreateKeyEx(HKEY_CURRENT_USER, (_T("Software\\Classes\\") + name + _T("\\Shell\\Open\\Command")).c_str(),
0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hk, NULL) != ERROR_SUCCESS)
+ {
return false;
+ }
bool ret = ::RegSetValueEx(hk, _T(""), 0, REG_SZ, (LPBYTE) app.c_str(), sizeof(TCHAR) * (app.length() + 1)) == ERROR_SUCCESS;
::RegCloseKey(hk);
@@ -1341,33 +1354,54 @@
return ret;
}
-bool registerHandler(const tstring& name) {
- bool ret = registerHandler_(name);
- if(!ret)
- LogManager::getInstance()->message(str(F_("Error registering %1%:// link handler") % Text::fromT(name)));
+/// @todo var template
+bool registerHandler(const tstring& name, const tstring& description, bool url, const tstring& prefix = Util::emptyStringT) {
+ bool ret = registerHandler_(name, description, url, prefix);
+ if(ret) {
+ regChanged();
+ } else {
+ LogManager::getInstance()->message(str(F_("Error registering the %1% handler") % Text::fromT(name)));
+ }
return ret;
}
+} // unnamed namespace
+
void WinUtil::registerHubHandlers() {
if(SETTING(URL_HANDLER)) {
if(!urlDcADCRegistered) {
- urlDcADCRegistered = registerHandler(_T("dchub")) && registerHandler(_T("adc")) && registerHandler(_T("adcs"));
+ urlDcADCRegistered = registerHandler(_T("dchub"), _T("URL:Direct Connect Protocol"), true) &&
+ registerHandler(_T("adc"), _T("URL:Direct Connect Protocol"), true) &&
+ registerHandler(_T("adcs"), _T("URL:Direct Connect Protocol"), true);
}
} else if(urlDcADCRegistered) {
- urlDcADCRegistered = !(
- (::SHDeleteKey(HKEY_CURRENT_USER, _T("Software\\Classes\\dchub")) == ERROR_SUCCESS) &&
- (::SHDeleteKey(HKEY_CURRENT_USER, _T("Software\\Classes\\adc")) == ERROR_SUCCESS) &&
- (::SHDeleteKey(HKEY_CURRENT_USER, _T("Software\\Classes\\adcs")) == ERROR_SUCCESS));
+ urlDcADCRegistered =
+ ::SHDeleteKey(HKEY_CURRENT_USER, _T("Software\\Classes\\dchub")) != ERROR_SUCCESS ||
+ ::SHDeleteKey(HKEY_CURRENT_USER, _T("Software\\Classes\\adc")) == ERROR_SUCCESS ||
+ ::SHDeleteKey(HKEY_CURRENT_USER, _T("Software\\Classes\\adcs")) == ERROR_SUCCESS;
+ regChanged();
}
}
void WinUtil::registerMagnetHandler() {
if(SETTING(MAGNET_REGISTER)) {
if(!urlMagnetRegistered) {
- urlMagnetRegistered = registerHandler(_T("magnet"));
+ urlMagnetRegistered = registerHandler(_T("magnet"), _T("URL:Magnet"), true);
}
} else if(urlMagnetRegistered) {
urlMagnetRegistered = ::SHDeleteKey(HKEY_CURRENT_USER, _T("Software\\Classes\\magnet")) != ERROR_SUCCESS;
+ regChanged();
+ }
+}
+
+void WinUtil::registerDcextHandler() {
+ if(SETTING(DCEXT_REGISTER)) {
+ if(!dcextRegistered) {
+ dcextRegistered = registerHandler(_T(".dcext"), _T("DC plugin"), false, _T("dcext:"));
+ }
+ } else if(dcextRegistered) {
+ dcextRegistered = ::SHDeleteKey(HKEY_CURRENT_USER, _T("Software\\Classes\\.dcext")) != ERROR_SUCCESS;
+ regChanged();
}
}
=== modified file 'win32/WinUtil.h'
--- win32/WinUtil.h 2013-03-30 15:55:28 +0000
+++ win32/WinUtil.h 2013-04-22 22:31:37 +0000
@@ -298,9 +298,10 @@
static void killHelpTooltip();
static pair<bool, string> getHelpText(unsigned id);
- // URL related
+ // file type & protocol associations
static void registerHubHandlers();
static void registerMagnetHandler();
+ static void registerDcextHandler();
static void addUserItems(Menu* menu, const HintedUserList& users, TabViewPtr parent, const StringList& dirs = StringList());
@@ -323,6 +324,7 @@
static bool urlDcADCRegistered;
static bool urlMagnetRegistered;
+ static bool dcextRegistered;
};
#endif // !defined(WIN_UTIL_H)