------------------------------------------------------------ revno: 3270 committer: poy <p...@123gen.com> 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)
_______________________________________________ Mailing list: https://launchpad.net/~linuxdcpp-team Post to : linuxdcpp-team@lists.launchpad.net Unsubscribe : https://launchpad.net/~linuxdcpp-team More help : https://help.launchpad.net/ListHelp