First commit
|
@ -0,0 +1,48 @@
|
|||
<?xml version="1.0"?>
|
||||
<manifest package="org.martchus.passwordmanager" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" android:versionCode="1" android:installLocation="auto">
|
||||
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="@string/app_name" android:icon="@drawable/icon">
|
||||
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:label="@string/app_name" android:screenOrientation="unspecified" android:launchMode="singleTop">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
<meta-data android:name="android.app.lib_name" android:value="passwordmanager"/>
|
||||
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
|
||||
<meta-data android:name="android.app.repository" android:value="default"/>
|
||||
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
|
||||
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
|
||||
<!-- Deploy Qt libs as part of package -->
|
||||
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
|
||||
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
|
||||
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
|
||||
<!-- Run with local libs -->
|
||||
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
|
||||
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
|
||||
<meta-data android:name="android.app.load_local_libs" android:value="-- %%INSERT_LOCAL_LIBS%% --"/>
|
||||
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
|
||||
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
|
||||
<!-- Messages maps -->
|
||||
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
|
||||
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
|
||||
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
|
||||
<!-- Messages maps -->
|
||||
|
||||
<!-- Splash screen -->
|
||||
<!--
|
||||
<meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/>
|
||||
-->
|
||||
<!-- Splash screen -->
|
||||
</activity>
|
||||
</application>
|
||||
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="20"/>
|
||||
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
|
||||
|
||||
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
|
||||
Remove the comment if you do not require these default permissions. -->
|
||||
<!-- %%INSERT_PERMISSIONS -->
|
||||
|
||||
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
|
||||
Remove the comment if you do not require these default features. -->
|
||||
<!-- %%INSERT_FEATURES -->
|
||||
|
||||
</manifest>
|
After Width: | Height: | Size: 3.8 KiB |
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Password Manager</string>
|
||||
</resources>
|
|
@ -0,0 +1,768 @@
|
|||
#include "cli.h"
|
||||
|
||||
#include <passwordfile/io/passwordfile.h>
|
||||
#include <passwordfile/io/cryptoexception.h>
|
||||
#include <passwordfile/io/parsingexception.h>
|
||||
#include <passwordfile/io/entry.h>
|
||||
#include <passwordfile/io/field.h>
|
||||
|
||||
#include <c++utilities/conversion/stringconversion.h>
|
||||
|
||||
#include <c++utilities/application/global.h>
|
||||
|
||||
#if defined(PLATFORM_UNIX)
|
||||
#include <unistd.h>
|
||||
#include <c++utilities/io/ansiescapecodes.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
using namespace std;
|
||||
using namespace std::placeholders;
|
||||
using namespace ConversionUtilities;
|
||||
using namespace Io;
|
||||
|
||||
namespace Cli {
|
||||
|
||||
InputMuter::InputMuter()
|
||||
{
|
||||
#if defined(PLATFORM_UNIX)
|
||||
tcgetattr(STDIN_FILENO, &m_attr);
|
||||
termios newAttr = m_attr;
|
||||
newAttr.c_lflag &= ~ECHO;
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &newAttr);
|
||||
#elif defined(PLATFORM_WINDOWS)
|
||||
m_cinHandle = GetStdHandle(STD_INPUT_HANDLE);
|
||||
m_mode = 0;
|
||||
GetConsoleMode(m_cinHandle, &m_mode);
|
||||
SetConsoleMode(m_cinHandle, m_mode & (~ENABLE_ECHO_INPUT));
|
||||
#endif
|
||||
}
|
||||
|
||||
InputMuter::~InputMuter()
|
||||
{
|
||||
#if defined(PLATFORM_UNIX)
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &m_attr);
|
||||
#elif defined(PLATFORM_WINDOWS)
|
||||
SetConsoleMode(m_cinHandle, m_mode);
|
||||
#endif
|
||||
}
|
||||
|
||||
void clearConsole()
|
||||
{
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
HANDLE hStdOut;
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
DWORD count;
|
||||
DWORD cellCount;
|
||||
COORD homeCoords = {0, 0};
|
||||
hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if(hStdOut == INVALID_HANDLE_VALUE) {
|
||||
return;
|
||||
}
|
||||
// get the number of cells in the current buffer
|
||||
if(!GetConsoleScreenBufferInfo(hStdOut, &csbi)) {
|
||||
return;
|
||||
}
|
||||
cellCount = csbi.dwSize.X * csbi.dwSize.Y;
|
||||
// fill the entire buffer with spaces
|
||||
if(!FillConsoleOutputCharacter(hStdOut, (TCHAR) ' ', cellCount, homeCoords, &count)) {
|
||||
return;
|
||||
}
|
||||
// fill the entire buffer with the current colors and attributes
|
||||
if(!FillConsoleOutputAttribute(hStdOut, csbi.wAttributes, cellCount, homeCoords, &count)) {
|
||||
return;
|
||||
}
|
||||
// move the cursor home
|
||||
SetConsoleCursorPosition(hStdOut, homeCoords);
|
||||
#else
|
||||
EscapeCodes::setCursor(cout);
|
||||
EscapeCodes::eraseDisplay(cout);
|
||||
#endif
|
||||
}
|
||||
|
||||
InteractiveCli::InteractiveCli() :
|
||||
m_o(cout),
|
||||
m_i(cin),
|
||||
m_currentEntry(nullptr),
|
||||
m_modified(false),
|
||||
m_quit(false)
|
||||
{}
|
||||
|
||||
void InteractiveCli::run(const string &file)
|
||||
{
|
||||
if(!file.empty()) {
|
||||
openFile(file, false);
|
||||
}
|
||||
string input;
|
||||
while(!m_quit) {
|
||||
getline(m_i, input);
|
||||
if(!input.empty()) {
|
||||
processCommand(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveCli::processCommand(const string &cmd)
|
||||
{
|
||||
#define CMD(value) !paramMissing && cmd == value
|
||||
#define CMD2(value1, value2) !paramMissing && (cmd == value1 || cmd == value2)
|
||||
#define CMD_P(value) !paramMissing && checkCommand(cmd, value, param, paramMissing)
|
||||
#define CMD2_P(value1, value2) !paramMissing && (checkCommand(cmd, value1, param, paramMissing) || checkCommand(cmd, value2, param, paramMissing))
|
||||
|
||||
string param;
|
||||
bool paramMissing = false;
|
||||
if(CMD2("quit", "q")) {
|
||||
quit();
|
||||
} else if(CMD("q!")) {
|
||||
m_quit = true;
|
||||
} else if(CMD("wq")) {
|
||||
saveFile();
|
||||
m_quit = true;
|
||||
} else if(CMD2("clear", "c")) {
|
||||
clearConsole();
|
||||
} else if(CMD2_P("openreadonly", "or")) {
|
||||
openFile(param, true);
|
||||
} else if(CMD2_P("open", "o")) {
|
||||
openFile(param, false);
|
||||
} else if(CMD2("close", "c")) {
|
||||
closeFile();
|
||||
} else if(CMD2("save", "w")) {
|
||||
saveFile();
|
||||
} else if(CMD2_P("create", "cr")) {
|
||||
createFile(param);
|
||||
} else if(CMD("chpassphrase")) {
|
||||
changePassphrase();
|
||||
} else if(CMD("rmpassphrase")) {
|
||||
removePassphrase();
|
||||
} else if(CMD("pwd")) {
|
||||
pwd();
|
||||
} else if(CMD_P("cd")) {
|
||||
cd(param);
|
||||
} else if(CMD("ls")) {
|
||||
ls();
|
||||
} else if(CMD2("tree", "t")) {
|
||||
tree();
|
||||
} else if(CMD2_P("mknode", "mkn")) {
|
||||
makeEntry(EntryType::Node, param);
|
||||
} else if(CMD2_P("mkaccount", "mka")) {
|
||||
makeEntry(EntryType::Account, param);
|
||||
} else if(CMD2("rmentry", "rme")) {
|
||||
removeEntry(".");
|
||||
} else if(CMD2_P("rmentry", "rme")) {
|
||||
removeEntry(param);
|
||||
} else if(CMD2_P("rnentry", "rne")) {
|
||||
renameEntry(param);
|
||||
} else if(CMD2_P("mventry", "me")) {
|
||||
moveEntry(param);
|
||||
} else if(CMD2_P("readfield", "rf")) {
|
||||
readField(param);
|
||||
} else if(CMD2_P("setfield", "sf")) {
|
||||
setField(false, param);
|
||||
} else if(CMD2_P("setfieldpw", "sp")) {
|
||||
setField(true, param);
|
||||
} else if(CMD2_P("rmfield", "rf")) {
|
||||
removeField(param);
|
||||
} else if(CMD2("help", "?")) {
|
||||
printHelp();
|
||||
} else if(paramMissing) {
|
||||
m_o << "parameter is missing" << endl;
|
||||
} else {
|
||||
m_o << "command is unknown" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
Entry *InteractiveCli::resolvePath(const string &path)
|
||||
{
|
||||
auto parts = splitString<vector<string> >(path, "/", EmptyPartsTreat::Merge);
|
||||
bool fromRoot = path.at(0) == '/';
|
||||
if(fromRoot && parts.empty()) {
|
||||
return m_file.rootEntry();
|
||||
} else {
|
||||
Entry *entry = fromRoot ? m_file.rootEntry() : m_currentEntry;
|
||||
for(const string &part : parts) {
|
||||
if(part == "..") {
|
||||
if(entry->parent()) {
|
||||
entry = entry->parent();
|
||||
} else {
|
||||
m_o << "can not resolve path; entry \"" << entry->label() << "\" is root" << endl;
|
||||
return nullptr;
|
||||
}
|
||||
} else if(part != ".") {
|
||||
switch(entry->type()) {
|
||||
case EntryType::Account:
|
||||
m_o << "can not resolve path; entry \"" << entry->label() << "\" is not a node entry" << endl;
|
||||
return nullptr;
|
||||
case EntryType::Node:
|
||||
for(Entry *child : (static_cast<NodeEntry *>(entry)->children())) {
|
||||
if(child->label() == part) {
|
||||
entry = child;
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
m_o << "can not resolve path; entry \"" << entry->label() << "\" has no child \"" << part << "\"" << endl;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
next:;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
bool InteractiveCli::checkCommand(const string &str, const char *phrase, std::string ¶m, bool ¶mMissing)
|
||||
{
|
||||
for(auto i = str.cbegin(), end = str.cend(); i != end; ++i, ++phrase) {
|
||||
if(*phrase == 0) {
|
||||
if(*i == ' ') {
|
||||
if(++i != end) {
|
||||
param.assign(i, end);
|
||||
return true;
|
||||
} else {
|
||||
paramMissing = true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else if(*i != *phrase) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
paramMissing = *phrase == 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
void InteractiveCli::openFile(const string &file, bool readOnly)
|
||||
{
|
||||
if(m_file.isOpen()) {
|
||||
m_o << "file \"" << m_file.path() << "\" currently open; close first" << endl;
|
||||
} else {
|
||||
m_file.setPath(file);
|
||||
try {
|
||||
try {
|
||||
m_file.open(readOnly);
|
||||
if(m_file.isEncryptionUsed()) {
|
||||
m_file.setPassword(askForPassphrase());
|
||||
}
|
||||
m_file.load();
|
||||
m_currentEntry = m_file.rootEntry();
|
||||
m_o << "file \"" << file << "\" opened" << endl;
|
||||
} catch (ParsingException &) {
|
||||
m_o << "error occured when parsing file \"" << file << "\"" << endl;
|
||||
throw;
|
||||
} catch (CryptoException &) {
|
||||
m_o << "error occured when decrypting file \"" << file << "\"" << endl;
|
||||
throw;
|
||||
} catch (ios_base::failure &) {
|
||||
m_o << "IO error occured when opening file \"" << file << "\"" << endl;
|
||||
throw;
|
||||
} catch (exception &) {
|
||||
m_o << "an unexpected exception occured when opening file \"" << file << "\"" << endl;
|
||||
terminate();
|
||||
}
|
||||
} catch(exception &e) {
|
||||
if(*e.what() != 0) {
|
||||
m_o << e.what() << endl;
|
||||
}
|
||||
m_file.clear();
|
||||
m_currentEntry = nullptr;
|
||||
}
|
||||
m_modified = false;
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveCli::closeFile()
|
||||
{
|
||||
if(m_file.isOpen()) {
|
||||
m_file.clear();
|
||||
m_currentEntry = nullptr;
|
||||
m_o << "file closed" << endl;
|
||||
} else {
|
||||
m_o << "no file was opened" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveCli::saveFile()
|
||||
{
|
||||
if(m_file.isOpen()) {
|
||||
try {
|
||||
try {
|
||||
m_file.save(*m_file.password());
|
||||
m_o << "file \"" << m_file.path() << "\" saved" << endl;
|
||||
} catch (ParsingException &) {
|
||||
m_o << "error occured when parsing file \"" << m_file.path() << "\"" << endl;
|
||||
throw;
|
||||
} catch (CryptoException &) {
|
||||
m_o << "error occured when encrypting file \"" << m_file.path() << "\"" << endl;
|
||||
throw;
|
||||
} catch (ios_base::failure &) {
|
||||
m_o << "IO error occured when saving file \"" << m_file.path() << "\"" << endl;
|
||||
throw;
|
||||
} catch (exception &) {
|
||||
m_o << "an unexpected exception occured when saving file \"" << m_file.path() << "\"" << endl;
|
||||
terminate();
|
||||
}
|
||||
} catch(exception &e) {
|
||||
if(*e.what() != 0) {
|
||||
m_o << e.what() << endl;
|
||||
}
|
||||
m_o << "file has been closed; try reopening the file" << endl;
|
||||
m_file.clear();
|
||||
}
|
||||
m_modified = false;
|
||||
} else {
|
||||
m_o << "nothing to save; no file opened or created" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveCli::createFile(const string &file)
|
||||
{
|
||||
if(m_file.isOpen()) {
|
||||
m_o << "file \"" << m_file.path() << "\" currently open; close first" << endl;
|
||||
} else {
|
||||
m_file.setPath(file);
|
||||
try {
|
||||
try {
|
||||
m_file.create();
|
||||
m_file.generateRootEntry();
|
||||
m_currentEntry = m_file.rootEntry();
|
||||
m_o << "file \"" << file << "\" created and opened" << endl;
|
||||
} catch (ios_base::failure &) {
|
||||
m_o << "IO error occured when creating file \"" << file << "\"" << endl;
|
||||
throw;
|
||||
} catch (exception &) {
|
||||
m_o << "an unexpected exception occured when creating file \"" << file << "\"" << endl;
|
||||
terminate();
|
||||
}
|
||||
} catch(exception &e) {
|
||||
if(*e.what() != 0) {
|
||||
m_o << e.what() << endl;
|
||||
}
|
||||
m_file.clear();
|
||||
m_currentEntry = nullptr;
|
||||
}
|
||||
m_modified = false;
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveCli::changePassphrase()
|
||||
{
|
||||
if(m_file.isOpen()) {
|
||||
try {
|
||||
m_file.setPassword(askForPassphrase(true));
|
||||
m_modified = true;
|
||||
m_o << "passphrase changed; use save to apply" << endl;
|
||||
} catch(runtime_error &) {
|
||||
m_o << "passphrase has not changed" << endl;
|
||||
}
|
||||
} else {
|
||||
m_o << "can not set passphrase; no file opened or created" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveCli::removePassphrase()
|
||||
{
|
||||
if(m_file.isOpen()) {
|
||||
if(*m_file.password()) {
|
||||
m_file.clearPassword();
|
||||
m_o << "passphrase removed; use save to apply" << endl;
|
||||
m_modified = true;
|
||||
} else {
|
||||
m_o << "nothing to remove; no passphrase present on current file" << endl;
|
||||
}
|
||||
} else {
|
||||
m_o << "nothing to remove; no file opened or created" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveCli::pwd()
|
||||
{
|
||||
if(m_file.isOpen()) {
|
||||
auto path = m_currentEntry->path();
|
||||
m_o << path.front() << ": /";
|
||||
path.pop_front();
|
||||
m_o << joinStrings(path, "/") << endl;
|
||||
} else {
|
||||
m_o << "no file open" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveCli::cd(const string &path)
|
||||
{
|
||||
if(m_file.isOpen()) {
|
||||
if(Entry *entry = resolvePath(path)) {
|
||||
m_currentEntry = entry;
|
||||
m_o << "changed to \"" << entry->label() << "\"" << endl;
|
||||
}
|
||||
} else {
|
||||
m_o << "can not change directory; no file open" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveCli::ls()
|
||||
{
|
||||
if(m_file.isOpen()) {
|
||||
switch(m_currentEntry->type()) {
|
||||
case EntryType::Account: {
|
||||
m_o << "fields:";
|
||||
for(const Field &field : static_cast<AccountEntry *>(m_currentEntry)->fields()) {
|
||||
m_o << " " << field.name();
|
||||
}
|
||||
break;
|
||||
} case EntryType::Node: {
|
||||
m_o << "entries:";
|
||||
for(const Entry *entry : static_cast<NodeEntry *>(m_currentEntry)->children()) {
|
||||
m_o << " " << entry->label();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_o << endl;
|
||||
} else {
|
||||
m_o << "can not list any entires; no file open" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveCli::tree()
|
||||
{
|
||||
if(m_file.isOpen()) {
|
||||
function<void(const Entry *entry, unsigned char level)> printEntries;
|
||||
printEntries = [&printEntries, this] (const Entry *entry, unsigned char level) {
|
||||
for(unsigned char i = 0; i < level; ++i) {
|
||||
m_o << " ";
|
||||
}
|
||||
m_o << entry->label() << endl;
|
||||
if(entry->type() == EntryType::Node) {
|
||||
for(const Entry *child : (static_cast<const NodeEntry *>(entry)->children())) {
|
||||
printEntries(child, level + 2);
|
||||
}
|
||||
}
|
||||
};
|
||||
printEntries(m_currentEntry, 0);
|
||||
} else {
|
||||
m_o << "can not print tree; no file open" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveCli::makeEntry(EntryType entryType, const string &label)
|
||||
{
|
||||
if(m_file.isOpen()) {
|
||||
switch(m_currentEntry->type()) {
|
||||
case EntryType::Node:
|
||||
switch(entryType) {
|
||||
case EntryType::Node:
|
||||
m_o << "node entry \"" << (new NodeEntry(label, static_cast<NodeEntry *>(m_currentEntry)))->label() << "\" created" << endl;
|
||||
break;
|
||||
case EntryType::Account:
|
||||
m_o << "account entry \"" << (new AccountEntry(label, static_cast<NodeEntry *>(m_currentEntry)))->label() << "\" created" << endl;
|
||||
break;
|
||||
}
|
||||
m_modified = true;
|
||||
break;
|
||||
case EntryType::Account:
|
||||
m_o << "can not make entry; current entry is no node entry" << endl;
|
||||
}
|
||||
} else {
|
||||
m_o << "can not make entry; no file open" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveCli::removeEntry(const string &path)
|
||||
{
|
||||
if(m_file.isOpen()) {
|
||||
if(Entry *entry = resolvePath(path)) {
|
||||
if(entry == m_file.rootEntry()) {
|
||||
m_o << "can not remove root entry" << endl;
|
||||
} else {
|
||||
if(entry == m_currentEntry) {
|
||||
m_currentEntry = entry->parent();
|
||||
}
|
||||
m_o << "removed entry \"" << entry->label() << "\"" << endl;
|
||||
delete entry;
|
||||
m_modified = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m_o << "can not remove entry; no file open" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveCli::renameEntry(const string &path)
|
||||
{
|
||||
if(m_file.isOpen()) {
|
||||
if(Entry *entry = resolvePath(path)) {
|
||||
string label;
|
||||
m_o << "enter new name: " << endl;
|
||||
getline(m_i, label);
|
||||
if(label.empty()) {
|
||||
m_o << "can not rename; new name is empty" << endl;
|
||||
} else {
|
||||
entry->setLabel(label);
|
||||
m_o << "entry renamed to \"" << entry->label() << "\"" << endl;
|
||||
m_modified = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m_o << "can not rename entry; no file open" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveCli::moveEntry(const string &path)
|
||||
{
|
||||
if(m_file.isOpen()) {
|
||||
if(Entry *entry = resolvePath(path)) {
|
||||
string newParentPath;
|
||||
m_o << "enter path of new parent: " << endl;
|
||||
getline(m_i, newParentPath);
|
||||
if(newParentPath.empty()) {
|
||||
m_o << "can not move; path of new parent is empty" << endl;
|
||||
} else {
|
||||
if(Entry *newParent = resolvePath(newParentPath)) {
|
||||
switch(newParent->type()) {
|
||||
case EntryType::Account:
|
||||
m_o << "can not move; new parent must be a node entry" << endl;
|
||||
break;
|
||||
case EntryType::Node:
|
||||
if(entry->parent() == entry) {
|
||||
m_o << "element not moved; parent doesn't change" << endl;
|
||||
} else if(entry->type() == EntryType::Node && newParent->isIndirectChildOf(static_cast<NodeEntry *>(entry))) {
|
||||
m_o << "can not move; new parent mustn't be child of the entry to move" << endl;
|
||||
} else {
|
||||
entry->setParent(static_cast<NodeEntry *>(newParent));
|
||||
m_o << "entry moved to \"" << newParent->label() << "\"" << endl;
|
||||
m_modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m_o << "can not rename entry; no file open" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveCli::readField(const string &fieldName)
|
||||
{
|
||||
if(m_file.isOpen()) {
|
||||
if(m_currentEntry->type() == EntryType::Account) {
|
||||
const vector<Field> &fields = static_cast<AccountEntry *>(m_currentEntry)->fields();
|
||||
bool valuesFound = false;
|
||||
for(const Field &field : fields) {
|
||||
if(field.name() == fieldName) {
|
||||
m_o << field.value() << endl;
|
||||
valuesFound = true;
|
||||
}
|
||||
}
|
||||
if(!valuesFound) {
|
||||
m_o << "field \"" << fieldName << "\" does not exist" << endl;
|
||||
}
|
||||
} else {
|
||||
m_o << "can not read field; current entry is no account entry" << endl;
|
||||
}
|
||||
} else {
|
||||
m_o << "can not read field; no file open" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveCli::setField(bool useMuter, const string &fieldName)
|
||||
{
|
||||
if(m_file.isOpen()) {
|
||||
if(m_currentEntry->type() == EntryType::Account) {
|
||||
vector<Field> &fields = static_cast<AccountEntry *>(m_currentEntry)->fields();
|
||||
unsigned int valuesFound = 0;
|
||||
string value;
|
||||
m_o << "enter new value: ";
|
||||
if(useMuter) {
|
||||
InputMuter m;
|
||||
getline(m_i, value);
|
||||
m_o << endl << "repeat: ";
|
||||
string repeat;
|
||||
getline(m_i, repeat);
|
||||
if(value != repeat) {
|
||||
m_o << "values do not match; field has not been altered" << endl;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
getline(m_i, value);
|
||||
}
|
||||
for(Field &field : fields) {
|
||||
if(field.name() == fieldName) {
|
||||
++valuesFound;
|
||||
if(valuesFound == 1) {
|
||||
field.setValue(value);
|
||||
} else {
|
||||
m_o << "enter new value for " << valuesFound << ". field (with the specified field): ";
|
||||
value.clear();
|
||||
if(useMuter) {
|
||||
InputMuter m;
|
||||
getline(m_i, value);
|
||||
m_o << endl << "repeat: ";
|
||||
string repeat;
|
||||
getline(m_i, repeat);
|
||||
if(value == repeat) {
|
||||
field.setValue(value);
|
||||
} else {
|
||||
m_o << "values do not match; field has not been altered" << endl;
|
||||
}
|
||||
} else {
|
||||
getline(m_i, value);
|
||||
field.setValue(value);
|
||||
}
|
||||
}
|
||||
field.setType(useMuter ? FieldType::Password : FieldType::Normal);
|
||||
}
|
||||
}
|
||||
switch(valuesFound) {
|
||||
case 0:
|
||||
fields.emplace_back(static_cast<AccountEntry *>(m_currentEntry), fieldName, value);
|
||||
if(useMuter) {
|
||||
fields.back().setType(FieldType::Password);
|
||||
}
|
||||
m_o << "new field with value inserted" << endl;
|
||||
break;
|
||||
case 1:
|
||||
m_o << "value updated" << endl;
|
||||
m_modified = true;
|
||||
break;
|
||||
default:
|
||||
m_o << valuesFound << " values updated" << endl;
|
||||
m_modified = true;
|
||||
}
|
||||
} else {
|
||||
m_o << "can not set field; current entry is no account entry" << endl;
|
||||
}
|
||||
} else {
|
||||
m_o << "can not set field; no file open" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveCli::removeField(const string &fieldName)
|
||||
{
|
||||
if(m_file.isOpen()) {
|
||||
if(m_currentEntry->type() == EntryType::Account) {
|
||||
vector<Field> &fields = static_cast<AccountEntry *>(m_currentEntry)->fields();
|
||||
unsigned int valuesFound = 0;
|
||||
for(Field &field : fields) {
|
||||
if(field.name() == fieldName) {
|
||||
++valuesFound;
|
||||
}
|
||||
}
|
||||
switch(valuesFound) {
|
||||
valuesFound = 0;
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
fields.erase(remove_if(fields.begin(), fields.end(), [&fieldName] (Field &field) {
|
||||
return field.name() == fieldName;
|
||||
}));
|
||||
break;
|
||||
default:
|
||||
fields.erase(remove_if(fields.begin(), fields.end(), [this, &fieldName, &valuesFound] (Field &field) {
|
||||
if(field.name() == fieldName) {
|
||||
m_o << "remove " << ++valuesFound << ". occurrence? [y]=yes, different key=no " << endl;
|
||||
string res;
|
||||
getline(m_i, res);
|
||||
return !(res == "y" || res == "yes");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
}
|
||||
switch(valuesFound) {
|
||||
case 0:
|
||||
m_o << "can not remove field; specified field \"" << fieldName << "\" not found" << endl;
|
||||
break;
|
||||
case 1:
|
||||
m_o << "field removed" << endl;
|
||||
m_modified = true;
|
||||
break;
|
||||
default:
|
||||
m_o << valuesFound << " fields removed" << endl;
|
||||
m_modified = true;
|
||||
}
|
||||
} else {
|
||||
m_o << "can not remove field; current entry is no account entry" << endl;
|
||||
}
|
||||
} else {
|
||||
m_o << "can not remove field; no file open" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveCli::printHelp()
|
||||
{
|
||||
m_o << "Available commands: \n"
|
||||
"quit,q quits the application\n"
|
||||
"q! forces the application to quit\n"
|
||||
"wq saves the current file and quits the application\n"
|
||||
"clear,c clears the console\n"
|
||||
"\n"
|
||||
"create,cr creates a new file at the specified path\n"
|
||||
"openreadonly opens the specified file (read-only)\n"
|
||||
"open,o opens the specified file\n"
|
||||
"close,cl closes the currently opened file\n"
|
||||
"\n"
|
||||
"save,w saves the currently opened file\n"
|
||||
"chpassphrase changes the passphrase\n"
|
||||
"rmpassphrase removes the passphrase\n"
|
||||
"\n"
|
||||
"pwd prints the path of the current entry\n"
|
||||
"cd changes the current entry\n"
|
||||
"ls lists the entries/fields of the current entry\n"
|
||||
"tree,t shows all child entries of the current entry\n"
|
||||
"mknode,mkn creates a node entry with the specified label in the current entry\n"
|
||||
"mkaccount,mka creates an account entry with the specified label in the current entry\n"
|
||||
"rmentry,rme removes the entry specified by its path\n"
|
||||
"rnentry,rne renames the entry specified by its path\n"
|
||||
"mventry,me moves the entry specified by its path\n"
|
||||
"readfield,rf reads the specified field of the current account\n"
|
||||
"setfield,sf sets the specified field of the current account\n"
|
||||
"setfieldpw,sp sets the specified password field of the current account\n"
|
||||
"rmfield,rf removes the specified field of the current account\n" << endl;
|
||||
}
|
||||
|
||||
void InteractiveCli::quit()
|
||||
{
|
||||
if(m_file.isOpen() && m_modified) {
|
||||
m_o << "file modified; use q! or wq" << endl;
|
||||
} else {
|
||||
m_quit = true;
|
||||
}
|
||||
}
|
||||
|
||||
string InteractiveCli::askForPassphrase(bool confirm)
|
||||
{
|
||||
if(confirm) {
|
||||
m_o << "enter new passphrase: ";
|
||||
} else {
|
||||
m_o << "enter passphrase: ";
|
||||
}
|
||||
m_o.flush();
|
||||
string input1;
|
||||
{
|
||||
InputMuter m;
|
||||
getline(m_i, input1);
|
||||
}
|
||||
m_o << endl;
|
||||
if(input1.empty()) {
|
||||
m_o << "you did not enter a passphrase" << endl;
|
||||
} else {
|
||||
if(confirm) {
|
||||
m_o << "confirm new passphrase: ";
|
||||
m_o.flush();
|
||||
string input2;
|
||||
{
|
||||
InputMuter m;
|
||||
getline(m_i, input2);
|
||||
}
|
||||
m_o << endl;
|
||||
if(input1 != input2) {
|
||||
m_o << "phrases do not match" << endl;
|
||||
throw runtime_error("confirmation failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
return input1;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
#ifndef CLI_CLI_H
|
||||
#define CLI_CLI_H
|
||||
|
||||
#include <passwordfile/io/passwordfile.h>
|
||||
|
||||
#include <c++utilities/application/global.h>
|
||||
|
||||
#if defined(PLATFORM_UNIX)
|
||||
#include <termios.h>
|
||||
#elif defined(PLATFORM_WINDOWS)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
|
||||
namespace ApplicationUtilities {
|
||||
|
||||
typedef std::vector<std::string> StringVector;
|
||||
|
||||
}
|
||||
|
||||
namespace Io {
|
||||
class Entry;
|
||||
enum class EntryType : int;
|
||||
}
|
||||
|
||||
namespace Cli {
|
||||
|
||||
class InputMuter
|
||||
{
|
||||
public:
|
||||
InputMuter();
|
||||
~InputMuter();
|
||||
|
||||
private:
|
||||
#if defined(PLATFORM_UNIX)
|
||||
termios m_attr;
|
||||
#elif defined(PLATFORM_WINDOWS)
|
||||
HANDLE m_cinHandle;
|
||||
DWORD m_mode;
|
||||
#endif
|
||||
};
|
||||
|
||||
void clearConsole();
|
||||
|
||||
class InteractiveCli
|
||||
{
|
||||
public:
|
||||
InteractiveCli();
|
||||
void run(const std::string &file = std::string());
|
||||
void openFile(const std::string &file, bool readOnly);
|
||||
void closeFile();
|
||||
void saveFile();
|
||||
void createFile(const std::string &file);
|
||||
void changePassphrase();
|
||||
void removePassphrase();
|
||||
void pwd();
|
||||
void cd(const std::string &path);
|
||||
void ls();
|
||||
void tree();
|
||||
void makeEntry(Io::EntryType entryType, const std::string &label);
|
||||
void removeEntry(const std::string &path);
|
||||
void renameEntry(const std::string &path);
|
||||
void moveEntry(const std::string &path);
|
||||
void readField(const std::string &fieldName);
|
||||
void setField(bool useMuter, const std::string &fieldName);
|
||||
void removeField(const std::string &fieldName);
|
||||
void printHelp();
|
||||
void quit();
|
||||
|
||||
private:
|
||||
void processCommand(const std::string &cmd);
|
||||
Io::Entry *resolvePath(const std::string &path);
|
||||
static bool checkCommand(const std::string &str, const char *phrase, std::string ¶m, bool ¶mMissing);
|
||||
std::string askForPassphrase(bool confirm = false);
|
||||
|
||||
std::ostream &m_o;
|
||||
std::istream &m_i;
|
||||
Io::PasswordFile m_file;
|
||||
Io::Entry *m_currentEntry;
|
||||
bool m_modified;
|
||||
bool m_quit;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // CLI_CLI_H
|
|
@ -0,0 +1,81 @@
|
|||
#include "draganddroptablewidget.h"
|
||||
|
||||
#include <QDropEvent>
|
||||
#include <QMimeData>
|
||||
#include <QMessageBox>
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
DragAndDropTableWidget::DragAndDropTableWidget(QWidget *parent) :
|
||||
QTableWidget(parent),
|
||||
m_insertEmptyRowAtEnd(false)
|
||||
{
|
||||
connect(this, &DragAndDropTableWidget::cellChanged,
|
||||
this, &DragAndDropTableWidget::processCellChanged);
|
||||
}
|
||||
|
||||
void DragAndDropTableWidget::insertEmptyRowAtEndAutomatically(bool value)
|
||||
{
|
||||
m_insertEmptyRowAtEnd = value;
|
||||
if(value) {
|
||||
processCellChanged(0,0);
|
||||
}
|
||||
}
|
||||
|
||||
bool DragAndDropTableWidget::hasEmptyRowAtEnd() const
|
||||
{
|
||||
bool hasEmptyRow = false;
|
||||
int rowCount = this->rowCount();
|
||||
if(rowCount > 0) {
|
||||
if(QTableWidgetItem *item = this->item(rowCount - 1, 0)) {
|
||||
hasEmptyRow = item->text().isEmpty();
|
||||
} else {
|
||||
hasEmptyRow = true;
|
||||
}
|
||||
}
|
||||
return hasEmptyRow;
|
||||
}
|
||||
|
||||
bool DragAndDropTableWidget::dropMimeData(int row, int column, const QMimeData *data, Qt::DropAction action)
|
||||
{
|
||||
if(data->hasText()) {
|
||||
if(QTableWidgetItem *item = this->item(row, column))
|
||||
item->setText(data->text());
|
||||
return false;
|
||||
} else {
|
||||
return QTableWidget::dropMimeData(row, column, data, action);
|
||||
}
|
||||
}
|
||||
|
||||
QStringList DragAndDropTableWidget::mimeTypes() const
|
||||
{
|
||||
return QTableWidget::mimeTypes() << QStringLiteral("text/plain");
|
||||
}
|
||||
|
||||
QMimeData *DragAndDropTableWidget::mimeData(const QList<QTableWidgetItem *> items) const
|
||||
{
|
||||
QString text;
|
||||
if(items.count() > 0) {
|
||||
QTableWidgetItem *lastItem = items.last();
|
||||
foreach(QTableWidgetItem *item, items) {
|
||||
if(!item->text().isEmpty()) {
|
||||
text.append(item->text());
|
||||
if(item != lastItem) {
|
||||
text.append(QStringLiteral("\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
QMimeData *data = QTableWidget::mimeData(items);
|
||||
data->setText(text);
|
||||
return data;
|
||||
}
|
||||
|
||||
void DragAndDropTableWidget::processCellChanged(int, int)
|
||||
{
|
||||
if(m_insertEmptyRowAtEnd && !hasEmptyRowAtEnd()) {
|
||||
insertRow(rowCount());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef DRAGANDDROPTABLEWIDGET_H
|
||||
#define DRAGANDDROPTABLEWIDGET_H
|
||||
|
||||
#include <QTableWidget>
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
class DragAndDropTableWidget : public QTableWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit DragAndDropTableWidget(QWidget *parent = nullptr);
|
||||
|
||||
void insertEmptyRowAtEndAutomatically(bool value);
|
||||
bool hasEmptyRowAtEnd() const;
|
||||
|
||||
protected:
|
||||
virtual bool dropMimeData(int row, int column, const QMimeData * data, Qt::DropAction action);
|
||||
virtual QStringList mimeTypes() const;
|
||||
virtual QMimeData *mimeData(const QList<QTableWidgetItem *> items) const;
|
||||
|
||||
private slots:
|
||||
void processCellChanged(int, int);
|
||||
|
||||
private:
|
||||
bool m_insertEmptyRowAtEnd;
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // DRAGANDDROPTABLEWIDGET_H
|
|
@ -0,0 +1,71 @@
|
|||
# template
|
||||
TEMPLATE = lib
|
||||
#dirs
|
||||
UI_DIR = ./gui
|
||||
MOC_DIR = ./moc
|
||||
OBJECTS_DIR = ./obj
|
||||
RCC_DIR = ./res
|
||||
# compiler flags
|
||||
QMAKE_CXXFLAGS += -std=c++11
|
||||
QMAKE_LFLAGS += -std=c++11
|
||||
unix {
|
||||
QMAKE_LFLAGS += "-Wl,--rpath=./"
|
||||
}
|
||||
# prefix
|
||||
targetprefix = .
|
||||
# target
|
||||
CONFIG(debug, debug|release) {
|
||||
TARGET = $$targetprefix/$${projectname}d
|
||||
} else {
|
||||
TARGET = $$targetprefix/$$projectname
|
||||
}
|
||||
# variables to check target architecture
|
||||
win32-g++:QMAKE_TARGET.arch = $$QMAKE_HOST.arch
|
||||
win32-g++-32:QMAKE_TARGET.arch = x86
|
||||
win32-g++-64:QMAKE_TARGET.arch = x86_64
|
||||
linux-g++:QMAKE_TARGET.arch = $$QMAKE_HOST.arch
|
||||
linux-g++-32:QMAKE_TARGET.arch = x86
|
||||
linux-g++-64:QMAKE_TARGET.arch = x86_64
|
||||
# configuration
|
||||
mobile {
|
||||
DEFINES += CONFIG_MOBILE
|
||||
} else:desktop {
|
||||
DEFINES += CONFIG_DESKTOP
|
||||
} else:android {
|
||||
CONFIG += mobile
|
||||
DEFINES += CONFIG_MOBILE
|
||||
} else {
|
||||
CONFIG += desktop
|
||||
DEFINES += CONFIG_DESKTOP
|
||||
}
|
||||
no-gui {
|
||||
QT -= gui
|
||||
DEFINES += GUI_NONE
|
||||
guiqtquick || guiqtwidgets {
|
||||
error("Can not use no-gui with guiqtquick or guiqtwidgets.")
|
||||
} else {
|
||||
message("Configured for no GUI support.")
|
||||
}
|
||||
} else {
|
||||
QT += gui
|
||||
mobile {
|
||||
CONFIG += guiqtquick
|
||||
}
|
||||
desktop {
|
||||
CONFIG += guiqtwidgets
|
||||
}
|
||||
}
|
||||
guiqtquick {
|
||||
message("Configured for Qt Quick GUI support.")
|
||||
greaterThan(QT_MAJOR_VERSION, 4): QT += quick
|
||||
CONFIG(debug, debug|release) {
|
||||
CONFIG += qml_debug
|
||||
}
|
||||
DEFINES += GUI_QTQUICK
|
||||
}
|
||||
guiqtwidgets {
|
||||
message("Configured for Qt widgets GUI support.")
|
||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||
DEFINES += GUI_QTWIDGETS
|
||||
DEFINES += MODEL_UNDO_SUPPORT
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef INITIATE_H
|
||||
#define INITIATE_H
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QString;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
int runWidgetsGui(int argc, char *argv[], const QString &file);
|
||||
|
||||
}
|
||||
|
||||
#endif // INITIATE_H
|
|
@ -0,0 +1,42 @@
|
|||
#include "initiate.h"
|
||||
|
||||
# include "gui/mainwindow.h"
|
||||
|
||||
#include <qtutilities/resources/resources.h>
|
||||
|
||||
#include <QTextCodec>
|
||||
#include <QApplication>
|
||||
#include <QFile>
|
||||
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
int runWidgetsGui(int argc, char *argv[], const QString &file)
|
||||
{
|
||||
// init application
|
||||
QApplication a(argc, argv);
|
||||
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
|
||||
QGuiApplication::setOrganizationName(QStringLiteral("Martchus"));
|
||||
QGuiApplication::setOrganizationDomain(QStringLiteral("http://martchus.netai.net/"));
|
||||
QGuiApplication::setApplicationName(QStringLiteral("Password Manager"));
|
||||
QGuiApplication::setApplicationVersion(QStringLiteral("2.0.5"));
|
||||
// load translation files
|
||||
TranslationFiles::loadQtTranslationFile();
|
||||
TranslationFiles::loadApplicationTranslationFile(QStringLiteral("passwordmanager"));
|
||||
// load the other resources
|
||||
QtUtilitiesResources::init();
|
||||
Theme::setup();
|
||||
// init widgets GUI
|
||||
QtGui::MainWindow w;
|
||||
w.show();
|
||||
if(!file.isEmpty()) {
|
||||
w.openFile(file);
|
||||
}
|
||||
// start event loop
|
||||
int res = a.exec();
|
||||
// cleanup resources
|
||||
QtUtilitiesResources::cleanup();
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include "passwordgeneratordialog.h"
|
||||
|
||||
#include <passwordfile/io/passwordfile.h>
|
||||
|
||||
#include <qtutilities/aboutdialog/aboutdialog.h>
|
||||
#include <c++utilities/io/binaryreader.h>
|
||||
#include <c++utilities/io/binarywriter.h>
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QMap>
|
||||
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QCloseEvent;
|
||||
class QTreeWidgetItem;
|
||||
class QUndoStack;
|
||||
class QUndoView;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Io {
|
||||
DECLARE_ENUM(EntryType, int)
|
||||
DECLARE_ENUM(FieldType, int)
|
||||
}
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
class FieldModel;
|
||||
class EntryModel;
|
||||
class EntryFilterModel;
|
||||
|
||||
namespace Ui {
|
||||
class MainWindow;
|
||||
}
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = nullptr);
|
||||
~MainWindow();
|
||||
|
||||
// file management
|
||||
bool openFile(const QString &path);
|
||||
void createFile(const QString &path, const QString &password);
|
||||
void createFile(const QString &path);
|
||||
|
||||
public slots:
|
||||
// file management
|
||||
bool createFile();
|
||||
void changePassword();
|
||||
bool saveFile();
|
||||
void exportFile();
|
||||
bool closeFile();
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *obj, QEvent *event);
|
||||
void closeEvent(QCloseEvent *event);
|
||||
void timerEvent(QTimerEvent *event);
|
||||
|
||||
private slots:
|
||||
// showing dialogs
|
||||
void showAboutDialog();
|
||||
void showPassowrdGeneratorDialog();
|
||||
void showOpenFileDialog();
|
||||
void showSaveFileDialog();
|
||||
void showUndoView();
|
||||
// file management
|
||||
bool showFile();
|
||||
// account/categories management
|
||||
void addAccount();
|
||||
void addCategory();
|
||||
void addEntry(Io::EntryType type);
|
||||
void removeEntry();
|
||||
void applyFilter(const QString &filterText);
|
||||
// row management
|
||||
void accountSelected(const QModelIndex &selected, const QModelIndex &);
|
||||
void insertRow();
|
||||
void removeRows();
|
||||
void markAsPasswordField();
|
||||
void markAsNormalField();
|
||||
void setFieldType(Io::FieldType fieldType);
|
||||
QString selectedFieldsString() const;
|
||||
void insertFields(const QString &fieldsString);
|
||||
void copyFieldsForXMilliSeconds(int x = 5000);
|
||||
void copyFields();
|
||||
void insertFieldsFromClipboard();
|
||||
// showing context menus
|
||||
void showTreeViewContextMenu();
|
||||
void showTableViewContextMenu();
|
||||
// recent entries menu
|
||||
void addRecentEntry(const QString &path);
|
||||
void openRecentFile();
|
||||
void clearRecent();
|
||||
// other
|
||||
void showContainingDirectory();
|
||||
void clearClipboard();
|
||||
void setSomethingChanged();
|
||||
|
||||
private:
|
||||
// showing conditional messages/prompts
|
||||
bool askForCreatingFile();
|
||||
bool showNoFileOpened();
|
||||
bool showNoAccount();
|
||||
// other
|
||||
void updateUiStatus();
|
||||
void applyDefaultExpanding(const QModelIndex &parent);
|
||||
|
||||
std::unique_ptr<Ui::MainWindow> m_ui;
|
||||
Io::PasswordFile m_file;
|
||||
FieldModel *m_fieldModel;
|
||||
EntryModel *m_entryModel;
|
||||
EntryFilterModel *m_entryFilterModel;
|
||||
QUndoStack *m_undoStack;
|
||||
QUndoView *m_undoView;
|
||||
bool m_somethingChanged;
|
||||
bool m_dontUpdateSelection;
|
||||
int m_clearClipboardTimer;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // MAINWINDOW_H
|
|
@ -0,0 +1,516 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>QtGui::MainWindow</class>
|
||||
<widget class="QMainWindow" name="QtGui::MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>848</width>
|
||||
<height>444</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Password Manager</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset resource="../resources/icons.qrc">
|
||||
<normaloff>:/icons/hicolor/128x128/apps/passwordmanager.png</normaloff>:/icons/hicolor/128x128/apps/passwordmanager.png</iconset>
|
||||
</property>
|
||||
<property name="locale">
|
||||
<locale language="English" country="UnitedStates"/>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralWidget">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QWidget" name="mainWidget" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="handleWidth">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="leftWidget" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="Widgets::ClearLineEdit" name="accountFilterLineEdit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>225</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>filter</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTreeView" name="treeView">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="dragEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="dragDropMode">
|
||||
<enum>QAbstractItemView::InternalMove</enum>
|
||||
</property>
|
||||
<property name="defaultDropAction">
|
||||
<enum>Qt::MoveAction</enum>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="animated">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="headerHidden">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="rightWidget" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTableView" name="tableView">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<property name="dragEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="dragDropMode">
|
||||
<enum>QAbstractItemView::DragDrop</enum>
|
||||
</property>
|
||||
<property name="defaultDropAction">
|
||||
<enum>Qt::CopyAction</enum>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menuBar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>848</width>
|
||||
<height>29</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuProgramm">
|
||||
<property name="title">
|
||||
<string>Fi&le</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuRecent">
|
||||
<property name="title">
|
||||
<string>&Recent</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="document-open-recent">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<addaction name="actionSepRecent"/>
|
||||
<addaction name="actionClearRecent"/>
|
||||
</widget>
|
||||
<addaction name="actionCreate"/>
|
||||
<addaction name="actionOpen"/>
|
||||
<addaction name="actionSave"/>
|
||||
<addaction name="actionSaveAs"/>
|
||||
<addaction name="actionExport"/>
|
||||
<addaction name="actionShowContainingDirectory"/>
|
||||
<addaction name="actionAlwaysCreateBackup"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionClose"/>
|
||||
<addaction name="actionChangepassword"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="menuRecent"/>
|
||||
<addaction name="actionQuit"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu">
|
||||
<property name="title">
|
||||
<string>?</string>
|
||||
</property>
|
||||
<addaction name="actionAbout"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuEdit">
|
||||
<property name="title">
|
||||
<string>Edit</string>
|
||||
</property>
|
||||
<addaction name="actionUndo"/>
|
||||
<addaction name="actionRedo"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionAddAccount"/>
|
||||
<addaction name="actionAddCategory"/>
|
||||
<addaction name="actionRemoveAccount"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionInsertRow"/>
|
||||
<addaction name="actionRemoveRows"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuTools">
|
||||
<property name="title">
|
||||
<string>&Tools</string>
|
||||
</property>
|
||||
<addaction name="actionPasswordGenerator"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuView">
|
||||
<property name="title">
|
||||
<string>&View</string>
|
||||
</property>
|
||||
<addaction name="actionHidePasswords"/>
|
||||
<addaction name="actionShowUndoStack"/>
|
||||
</widget>
|
||||
<addaction name="menuProgramm"/>
|
||||
<addaction name="menuEdit"/>
|
||||
<addaction name="menuView"/>
|
||||
<addaction name="menuTools"/>
|
||||
<addaction name="menu"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusBar">
|
||||
<property name="sizeGripEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<action name="actionOpen">
|
||||
<property name="icon">
|
||||
<iconset theme="document-open">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Open ...</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+O</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionQuit">
|
||||
<property name="icon">
|
||||
<iconset theme="application-exit">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Quit</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Q</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAbout">
|
||||
<property name="icon">
|
||||
<iconset theme="help-about">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&About</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSave">
|
||||
<property name="icon">
|
||||
<iconset theme="document-save">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Save</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+S</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCreate">
|
||||
<property name="icon">
|
||||
<iconset theme="document-new">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&New ...</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+N</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionClose">
|
||||
<property name="icon">
|
||||
<iconset theme="window-close">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Close</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Shift+Q</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionChangepassword">
|
||||
<property name="icon">
|
||||
<iconset theme="dialog-password">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Change &password ...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAddAccount">
|
||||
<property name="icon">
|
||||
<iconset theme="list-add">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Add new account</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Shift+A</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSaveAs">
|
||||
<property name="icon">
|
||||
<iconset theme="document-save-as">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Sa&ve as ...</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Shift+S</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRemoveAccount">
|
||||
<property name="icon">
|
||||
<iconset theme="list-remove">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="iconText">
|
||||
<string>Remove selected entry</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Remove selected entry</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionInsertRow">
|
||||
<property name="icon">
|
||||
<iconset theme="insert-text">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Insert field</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRemoveRows">
|
||||
<property name="icon">
|
||||
<iconset theme="list-remove">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Remove selected &field(s)</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPasswordGenerator">
|
||||
<property name="text">
|
||||
<string>&Password generator</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionClearRecent">
|
||||
<property name="icon">
|
||||
<iconset theme="edit-clear">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Clear list</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAlwaysCreateBackup">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Always create backup</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExport">
|
||||
<property name="icon">
|
||||
<iconset theme="document-export">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Export ...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionShowContainingDirectory">
|
||||
<property name="icon">
|
||||
<iconset theme="folder">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Show containing &directory</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionUndo">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="edit-undo">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Undo</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRedo">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="edit-redo">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Redo</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAddCategory">
|
||||
<property name="icon">
|
||||
<iconset theme="list-add">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add &new category</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionShowUndoStack">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Show undo stack</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionHidePasswords">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Hide passwords</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSepRecent"/>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>Widgets::ClearLineEdit</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header location="global">qtutilities/widgets/clearlineedit.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../resources/icons.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -0,0 +1,169 @@
|
|||
#include "passwordgeneratordialog.h"
|
||||
#include "ui_passwordgeneratordialog.h"
|
||||
|
||||
#include <passwordfile/io/cryptoexception.h>
|
||||
|
||||
#include <c++utilities/conversion/binaryconversion.h>
|
||||
|
||||
#include <openssl/rand.h>
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QClipboard>
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
|
||||
using namespace std;
|
||||
using namespace Io;
|
||||
using namespace Util;
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
const char smallLetters[] = {'a','b','c','d','e','f',
|
||||
'g','h','i','j','k',
|
||||
'l','m','n','o','p',
|
||||
'q','r','s','t','u',
|
||||
'v','w','x','y','z'};
|
||||
|
||||
const char capitalLetters[] = {'A','B','C','D','E','F',
|
||||
'G','H','I','J','K',
|
||||
'L','M','N','O','P',
|
||||
'Q','R','S','T','U',
|
||||
'V','W','X','Y','Z'};
|
||||
|
||||
const char digits[] = {'0','1','2','3','4',
|
||||
'5','6','7','8','9'};
|
||||
|
||||
/*!
|
||||
* \class PasswordGeneratorDialog
|
||||
* \brief The PasswordGeneratorDialog class provides a password generation dialog.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \brief Constructs a new password generator dialog.
|
||||
*/
|
||||
PasswordGeneratorDialog::PasswordGeneratorDialog(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
m_ui(new Ui::PasswordGeneratorDialog)
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
#ifdef Q_OS_WIN32
|
||||
setStyleSheet(QStringLiteral("* { font: 9pt \"Segoe UI\", \"Sans\"; } #mainFrame { border: none; background: white; } #bottomFrame { background-color: #F0F0F0; border-top: 1px solid #DFDFDF; } #mainFrame #captionNameLabel, QMessageBox QLabel, QCommandLinkButton { font-size: 12pt; color: #003399; font-weight: normal; } #atLeastOneOfEachCategoryFrame { border-top: 1px solid #eee; }"));
|
||||
#else
|
||||
setStyleSheet(QStringLiteral("#mainFrame #captionNameLabel { font-weight: bold; }"));
|
||||
#endif
|
||||
|
||||
setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint);
|
||||
|
||||
connect(m_ui->copyPasswordCommandLinkButton, SIGNAL(clicked()), this, SLOT(copyPassword()));
|
||||
connect(m_ui->generatePassowordCommandLinkButton, SIGNAL(clicked()), this, SLOT(generateNewPassword()));
|
||||
connect(m_ui->useCapitalLettersCheckBox, SIGNAL(stateChanged(int)), this, SLOT(handleCheckedCategoriesChanged()));
|
||||
connect(m_ui->useDigitsCheckBox, SIGNAL(stateChanged(int)), this, SLOT(handleCheckedCategoriesChanged()));
|
||||
connect(m_ui->otherCharsLineEdit, SIGNAL(editingFinished()), this, SLOT(handleCheckedCategoriesChanged()));
|
||||
connect(m_ui->useSmallLettersCheckBox, SIGNAL(stateChanged(int)), this, SLOT(handleCheckedCategoriesChanged()));
|
||||
connect(m_ui->passwordLineEdit, SIGNAL(textChanged(QString)), this, SLOT(handlePasswordChanged()));
|
||||
|
||||
handlePasswordChanged();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Destroys the dialog.
|
||||
*/
|
||||
PasswordGeneratorDialog::~PasswordGeneratorDialog()
|
||||
{
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Generates and shows a new password.
|
||||
*/
|
||||
void PasswordGeneratorDialog::generateNewPassword()
|
||||
{
|
||||
int length = m_ui->LengthSpinBox->value();
|
||||
if(length > 0) {
|
||||
if(m_charset.empty()) {
|
||||
bool useSmallLetters = m_ui->useSmallLettersCheckBox->isChecked();
|
||||
bool useCapitalLetters = m_ui->useCapitalLettersCheckBox->isChecked();
|
||||
bool useDigits = m_ui->useDigitsCheckBox->isChecked();
|
||||
QString otherChars = m_ui->otherCharsLineEdit->text();
|
||||
int charsetSize = otherChars.length();
|
||||
if(useSmallLetters) {
|
||||
charsetSize += sizeof(smallLetters);
|
||||
}
|
||||
if(useCapitalLetters) {
|
||||
charsetSize += sizeof(capitalLetters);
|
||||
}
|
||||
if(useDigits) {
|
||||
charsetSize += sizeof(digits);
|
||||
}
|
||||
m_charset.reserve(charsetSize);
|
||||
if(useSmallLetters) {
|
||||
m_charset.insert(m_charset.end(), std::begin(smallLetters), std::end(smallLetters));
|
||||
}
|
||||
if(useCapitalLetters) {
|
||||
m_charset.insert(m_charset.end(), std::begin(capitalLetters), std::end(capitalLetters));
|
||||
}
|
||||
if(useDigits) {
|
||||
m_charset.insert(m_charset.end(), std::begin(digits), std::end(digits));
|
||||
}
|
||||
char charval;
|
||||
foreach(QChar qchar, otherChars) {
|
||||
charval = qchar.toLatin1();
|
||||
if(charval != '\x00' && charval != ' ' && std::find(m_charset.begin(), m_charset.end(), charval) == m_charset.end()) {
|
||||
m_charset.push_back(charval);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!m_charset.empty()) {
|
||||
try {
|
||||
default_random_engine rng(m_random());
|
||||
uniform_int_distribution<> dist(0, m_charset.size() - 1);
|
||||
auto randchar = [this, &dist, &rng]() {
|
||||
return m_charset[dist(rng)];
|
||||
};
|
||||
string res(length, 0);
|
||||
generate_n(res.begin(), length, randchar);
|
||||
m_ui->passwordLineEdit->setText(QString::fromLatin1(res.c_str()));
|
||||
} catch(const CryptoException &ex) {
|
||||
QMessageBox::warning(this, QApplication::applicationName(), tr("Failed to generate password.\nOpenSSL error: %1").arg(QString::fromLocal8Bit(ex.what())));
|
||||
}
|
||||
} else {
|
||||
QMessageBox::warning(this, QApplication::applicationName(), tr("You have to select at least one checkbox."));
|
||||
}
|
||||
} else {
|
||||
QMessageBox::warning(this, QApplication::applicationName(), tr("The length has to be at least one."));
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Handles when the user checked or unchecked a category.
|
||||
*/
|
||||
void PasswordGeneratorDialog::handleCheckedCategoriesChanged()
|
||||
{
|
||||
m_ui->generatePassowordCommandLinkButton->setEnabled(m_ui->useCapitalLettersCheckBox->isChecked()
|
||||
|| m_ui->useDigitsCheckBox->isChecked()
|
||||
|| m_ui->useSmallLettersCheckBox->isChecked()
|
||||
|| !m_ui->otherCharsLineEdit->text().isEmpty());
|
||||
m_charset.clear();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Handles when the password changed.
|
||||
*/
|
||||
void PasswordGeneratorDialog::handlePasswordChanged()
|
||||
{
|
||||
m_ui->copyPasswordCommandLinkButton->setEnabled(m_ui->passwordLineEdit->text().count() > 0);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Copies the current password to the clipboard.
|
||||
*/
|
||||
void PasswordGeneratorDialog::copyPassword()
|
||||
{
|
||||
QClipboard *cb = QApplication::clipboard();
|
||||
cb->setText(m_ui->passwordLineEdit->text());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
#ifndef PASSWORDGENERATORDIALOG_H
|
||||
#define PASSWORDGENERATORDIALOG_H
|
||||
|
||||
#include <passwordfile/util/opensslrandomdevice.h>
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
namespace Ui {
|
||||
class PasswordGeneratorDialog;
|
||||
}
|
||||
|
||||
class PasswordGeneratorDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PasswordGeneratorDialog(QWidget *parent = 0);
|
||||
~PasswordGeneratorDialog();
|
||||
|
||||
private slots:
|
||||
void generateNewPassword();
|
||||
void handleCheckedCategoriesChanged();
|
||||
void handlePasswordChanged();
|
||||
void copyPassword();
|
||||
|
||||
private:
|
||||
Ui::PasswordGeneratorDialog *m_ui;
|
||||
std::vector<char> m_charset;
|
||||
Util::OpenSslRandomDevice m_random;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // PASSWORDGENERATORDIALOG_H
|
|
@ -0,0 +1,349 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>QtGui::PasswordGeneratorDialog</class>
|
||||
<widget class="QDialog" name="QtGui::PasswordGeneratorDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>512</width>
|
||||
<height>350</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>43</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>512</width>
|
||||
<height>350</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>512</width>
|
||||
<height>350</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Password Generator</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset resource="../resources/icons.qrc">
|
||||
<normaloff>:/icons/hicolor/128x128/apps/passwordmanager.png</normaloff>:/icons/hicolor/128x128/apps/passwordmanager.png</iconset>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="locale">
|
||||
<locale language="English" country="UnitedStates"/>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QFrame" name="mainFrame">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QFrame" name="informationFrame">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QFrame" name="checkboxFrame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="useSmallLettersCheckBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use small letters</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="useCapitalLettersCheckBox">
|
||||
<property name="text">
|
||||
<string>Use capital letters</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="useDigitsCheckBox">
|
||||
<property name="text">
|
||||
<string>Use digits</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="lengthFrame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="horizontalSpacing">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="lengtLabel">
|
||||
<property name="text">
|
||||
<string>Length</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Other characters to be used</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="otherCharsLineEdit">
|
||||
<property name="text">
|
||||
<string>!"§$%&/()=?;:_'*~’#,.-+<>|</string>
|
||||
</property>
|
||||
<property name="maxLength">
|
||||
<number>50</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="LengthSpinBox">
|
||||
<property name="minimum">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>256</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>10</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="passwordFrame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<property name="topMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="passwordLabel">
|
||||
<property name="text">
|
||||
<string><b>Password:</b></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="passwordLineEdit"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCommandLinkButton" name="generatePassowordCommandLinkButton">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Generate new password</string>
|
||||
</property>
|
||||
<property name="description">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCommandLinkButton" name="copyPasswordCommandLinkButton">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Copy password</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="bottomFrame">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>216</width>
|
||||
<height>14</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="closePushbutton">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Segoe UI,Sans</family>
|
||||
<pointsize>9</pointsize>
|
||||
<weight>50</weight>
|
||||
<italic>false</italic>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="window-close">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../resources/icons.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>closePushbutton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>QtGui::PasswordGeneratorDialog</receiver>
|
||||
<slot>close()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>464</x>
|
||||
<y>278</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>255</x>
|
||||
<y>149</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
|
@ -0,0 +1,17 @@
|
|||
#include "stacksupport.h"
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
/*!
|
||||
* \class StackSupport
|
||||
* \brief The StackSupport class is used as base class for models supporting undoing of changes via QUndoStack.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \brief Constructs a new stack support with the specified \a undoStack.
|
||||
*/
|
||||
StackSupport::StackSupport(QUndoStack *undoStack) :
|
||||
m_undoStack(undoStack)
|
||||
{}
|
||||
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
#ifndef QTGUI_STACKSUPPORT_H
|
||||
#define QTGUI_STACKSUPPORT_H
|
||||
|
||||
#include "undocommands.h"
|
||||
|
||||
#include <QUndoStack>
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
class StackAbsorper;
|
||||
|
||||
class StackSupport
|
||||
{
|
||||
friend class StackAbsorper;
|
||||
public:
|
||||
StackSupport(QUndoStack *undoStack = nullptr);
|
||||
|
||||
protected:
|
||||
QUndoStack *undoStack();
|
||||
bool push(CustomUndoCommand *command);
|
||||
void clearUndoStack();
|
||||
|
||||
private:
|
||||
QUndoStack *m_undoStack;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Returns the undo stack for the current instance.
|
||||
*/
|
||||
inline QUndoStack *StackSupport::undoStack()
|
||||
{
|
||||
return m_undoStack;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Pushes the specified custom undo \a command to the undo stack and returns whether the redo action was successful.
|
||||
*/
|
||||
inline bool StackSupport::push(CustomUndoCommand *command)
|
||||
{
|
||||
if(m_undoStack) {
|
||||
if(command->isNoop()) {
|
||||
return true; // doing nothing can never fail
|
||||
} else {
|
||||
m_undoStack->push(command);
|
||||
return command->redoResult();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Clears the undo stack.
|
||||
*/
|
||||
inline void StackSupport::clearUndoStack()
|
||||
{
|
||||
if(m_undoStack) {
|
||||
m_undoStack->clear();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief The StackAbsorper class is used by the CustomUndoCommand class to prevent infinite recursion when pushing
|
||||
* a new command to the stack.
|
||||
*/
|
||||
class StackAbsorper
|
||||
{
|
||||
public:
|
||||
StackAbsorper(StackSupport *supported);
|
||||
~StackAbsorper();
|
||||
QUndoStack *stack();
|
||||
private:
|
||||
StackSupport *m_supported;
|
||||
QUndoStack *m_stack;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Detaches the undo stack from the specified stack support temporary.
|
||||
*/
|
||||
inline StackAbsorper::StackAbsorper(StackSupport *supported) :
|
||||
m_supported(supported),
|
||||
m_stack(supported->m_undoStack)
|
||||
{
|
||||
m_supported->m_undoStack = nullptr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Restores the undo stack of the stack support.
|
||||
*/
|
||||
inline StackAbsorper::~StackAbsorper()
|
||||
{
|
||||
m_supported->m_undoStack = m_stack;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the stack for the current instance.
|
||||
*/
|
||||
inline QUndoStack *StackAbsorper::stack()
|
||||
{
|
||||
return m_stack;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // QTGUI_STACKSUPPORT_H
|
|
@ -0,0 +1,458 @@
|
|||
#include "undocommands.h"
|
||||
#include "stacksupport.h"
|
||||
#include "model/fieldmodel.h"
|
||||
#include "model/entrymodel.h"
|
||||
|
||||
#include <passwordfile/io/entry.h>
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
using namespace std;
|
||||
using namespace Io;
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
/*!
|
||||
* \class CustomUndoCommand
|
||||
* \brief The CustomUndoCommand class acts as base class for undo commands used within the
|
||||
* models of the application.
|
||||
* \sa http://qt-project.org/doc/qt-5/qundocommand.html
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \brief Constructs a new custom undo command with the specified \a stackSupport.
|
||||
*/
|
||||
CustomUndoCommand::CustomUndoCommand(StackSupport *stackSupport) :
|
||||
m_stackSupport(stackSupport),
|
||||
m_redoResult(false),
|
||||
m_undoResult(true),
|
||||
m_noop(false)
|
||||
{}
|
||||
|
||||
void CustomUndoCommand::redo()
|
||||
{
|
||||
if(m_undoResult) {
|
||||
StackAbsorper stackAbsorper(m_stackSupport);
|
||||
m_redoResult = internalRedo();
|
||||
}
|
||||
}
|
||||
|
||||
void CustomUndoCommand::undo()
|
||||
{
|
||||
if(m_redoResult) {
|
||||
StackAbsorper stackAbsorper(m_stackSupport);
|
||||
m_undoResult = internalUndo();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \fn CustomUndoCommand::internalRedo()
|
||||
* \brief This method is internally called to perform the redo action.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \fn CustomUndoCommand::internalUndo()
|
||||
* \brief This method is internally called to perform the undo action.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \class FieldModelSetValueCommand
|
||||
* \brief Sets the value for the specified index and role in the specified field model.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \brief Constructs a new command.
|
||||
*/
|
||||
FieldModelSetValueCommand::FieldModelSetValueCommand(FieldModel *model, const QModelIndex &index, const QVariant &value, int role) :
|
||||
CustomUndoCommand(model),
|
||||
m_account(model->accountEntry()),
|
||||
m_model(model),
|
||||
m_row(index.row()),
|
||||
m_col(index.column()),
|
||||
m_newValue(value),
|
||||
m_oldValue(model->data(index, role)),
|
||||
m_role(role)
|
||||
{
|
||||
QString fieldName = model->index(m_row, 0, index.parent()).data().toString();
|
||||
switch(role) {
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
switch(m_col) {
|
||||
case 0:
|
||||
if(m_oldValue.toString().isEmpty()) {
|
||||
setText(QApplication::translate("undocommands", "setting field name to »%1«").arg(m_newValue.toString()));
|
||||
} else {
|
||||
setText(QApplication::translate("undocommands", "setting field name »%1« to »%2«").arg(m_oldValue.toString(), m_newValue.toString()));
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if(fieldName.isEmpty()) {
|
||||
setText(QApplication::translate("undocommands", "setting value of empty field"));
|
||||
} else {
|
||||
setText(QApplication::translate("undocommands", "setting value of »%1« field").arg(fieldName));
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case FieldTypeRole:
|
||||
setText(QApplication::translate("undocommands", "setting type of »%1« field").arg(fieldName));
|
||||
break;
|
||||
default:
|
||||
setText(QApplication::translate("undocommands", "setting field property in row »%1«").arg(m_row + 1));
|
||||
}
|
||||
setNoop(m_oldValue == m_newValue);
|
||||
}
|
||||
|
||||
bool FieldModelSetValueCommand::internalRedo()
|
||||
{
|
||||
m_model->setAccountEntry(m_account);
|
||||
return m_model->setData(m_model->index(m_row, m_col), m_newValue, m_role);
|
||||
}
|
||||
|
||||
bool FieldModelSetValueCommand::internalUndo()
|
||||
{
|
||||
m_model->setAccountEntry(m_account);
|
||||
return m_model->setData(m_model->index(m_row, m_col), m_oldValue, m_role);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \class FieldModelInsertRowsCommand
|
||||
* \brief Inserts the specified number of rows before the specified row in the specified field model.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \brief Constructs a new command.
|
||||
*/
|
||||
FieldModelInsertRowsCommand::FieldModelInsertRowsCommand(FieldModel *model, int row, int count) :
|
||||
CustomUndoCommand(model),
|
||||
m_account(model->accountEntry()),
|
||||
m_model(model),
|
||||
m_row(row),
|
||||
m_count(count)
|
||||
{
|
||||
setText(QApplication::translate("undocommands", "insertion of %1 row(s) before row %2", 0, count).arg(count).arg(row + 1));
|
||||
}
|
||||
|
||||
bool FieldModelInsertRowsCommand::internalRedo()
|
||||
{
|
||||
m_model->setAccountEntry(m_account);
|
||||
return m_model->insertRows(m_row, m_count, QModelIndex());
|
||||
}
|
||||
|
||||
bool FieldModelInsertRowsCommand::internalUndo()
|
||||
{
|
||||
m_model->setAccountEntry(m_account);
|
||||
return m_model->removeRows(m_row, m_count, QModelIndex());
|
||||
}
|
||||
|
||||
/*!
|
||||
* \class FieldModelRemoveRowsCommand
|
||||
* \brief Removes the specified number of rows at the specified row in the specified field model.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \brief Constructs a new command.
|
||||
*/
|
||||
FieldModelRemoveRowsCommand::FieldModelRemoveRowsCommand(FieldModel *model, int row, int count) :
|
||||
CustomUndoCommand(model),
|
||||
m_account(model->accountEntry()),
|
||||
m_model(model),
|
||||
m_row(row),
|
||||
m_count(count)
|
||||
{
|
||||
if(count == 1) {
|
||||
setText(QApplication::translate("undocommands", "removal of row %1", 0, count).arg(row + 1));
|
||||
} else {
|
||||
setText(QApplication::translate("undocommands", "removal of the rows %1 to %2", 0, count).arg(row + 1).arg(row + count));
|
||||
}
|
||||
}
|
||||
|
||||
bool FieldModelRemoveRowsCommand::internalRedo()
|
||||
{
|
||||
m_model->setAccountEntry(m_account);
|
||||
if(m_values.isEmpty()) {
|
||||
for(int row = m_row, end = m_row + m_count; row < end; ++row) {
|
||||
if(const Field *field = m_model->field(row)) {
|
||||
m_values << Field(*field);
|
||||
}
|
||||
}
|
||||
}
|
||||
return m_model->removeRows(m_row, m_count, QModelIndex());
|
||||
}
|
||||
|
||||
bool FieldModelRemoveRowsCommand::internalUndo()
|
||||
{
|
||||
m_model->setAccountEntry(m_account);
|
||||
bool res = m_model->insertRows(m_row, m_count, QModelIndex());
|
||||
for(int row = m_row, end = m_row + m_count, value = 0, values = m_values.size(); row < end && value < values; ++row, ++value) {
|
||||
m_model->setData(m_model->index(row, 0), QString::fromStdString(m_values.at(value).name()), Qt::EditRole);
|
||||
m_model->setData(m_model->index(row, 1), QString::fromStdString(m_values.at(value).value()), Qt::EditRole);
|
||||
m_model->setData(m_model->index(row, 0), static_cast<int>(m_values.at(value).type()), FieldTypeRole);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Stores the entry path for the specified \a model and \a index in \a res.
|
||||
*/
|
||||
void indexToPath(EntryModel *model, const QModelIndex &index, list<string> &res)
|
||||
{
|
||||
res.clear();
|
||||
if(Entry *entry = model->entry(index)) {
|
||||
entry->path(res);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Fetches the entry for the specified \a model and \a path.
|
||||
* \remarks The \a path will be modified. To prevent this use entryFromPathCpy().
|
||||
*/
|
||||
Entry *entryFromPath(EntryModel *model, list<string> &path)
|
||||
{
|
||||
if(NodeEntry *rootEntry = model->rootEntry()) {
|
||||
return rootEntry->entryByPath(path);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Fetches the entry for the specified \a model and \a path.
|
||||
*/
|
||||
Entry *entryFromPathCpy(EntryModel *model, list<string> path)
|
||||
{
|
||||
return entryFromPath(model, path);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \class EntryModelSetValueCommand
|
||||
* \brief Sets the value for the specified index and role in the specified entry model.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \brief Constructs a new command.
|
||||
*/
|
||||
EntryModelSetValueCommand::EntryModelSetValueCommand(EntryModel *model, const QModelIndex &index, const QVariant &value, int role) :
|
||||
CustomUndoCommand(model),
|
||||
m_model(model),
|
||||
m_newValue(value),
|
||||
m_oldValue(model->data(index, role)),
|
||||
m_role(role)
|
||||
{
|
||||
indexToPath(model, index, m_path);
|
||||
switch(role) {
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
if(m_oldValue.toString().isEmpty()) {
|
||||
setText(QApplication::translate("undocommands", "setting entry name to »%1«").arg(m_newValue.toString()));
|
||||
} else {
|
||||
setText(QApplication::translate("undocommands", "setting entry name from »%1« to »%2«").arg(m_oldValue.toString(), m_newValue.toString()));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
QString name = model->data(model->index(index.row(), 0, index.parent()), Qt::DisplayRole).toString();
|
||||
if(name.isEmpty()) {
|
||||
setText(QApplication::translate("undocommands", "setting property of an entry"));
|
||||
} else {
|
||||
setText(QApplication::translate("undocommands", "setting property of entry »%1«").arg(name));
|
||||
}
|
||||
}
|
||||
setNoop(m_oldValue == m_newValue);
|
||||
}
|
||||
|
||||
bool EntryModelSetValueCommand::internalRedo()
|
||||
{
|
||||
if(Entry *entry = entryFromPath(m_model, m_path)) {
|
||||
bool res = m_model->setData(m_model->index(entry), m_newValue, m_role);
|
||||
m_path.clear();
|
||||
entry->path(m_path);
|
||||
return res;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EntryModelSetValueCommand::internalUndo()
|
||||
{
|
||||
if(Entry *entry = entryFromPath(m_model, m_path)) {
|
||||
bool res = m_model->setData(m_model->index(entry), m_oldValue, m_role);
|
||||
m_path.clear();
|
||||
entry->path(m_path);
|
||||
return res;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \class EntryModelInsertRowsCommand
|
||||
* \brief Modifies the specified number of rows before the specified row in the specified entry model under the specified parent.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \brief Constructs a new command.
|
||||
*/
|
||||
EntryModelModifyRowsCommand::EntryModelModifyRowsCommand(EntryModel *model, int row, int count, const QModelIndex &parent) :
|
||||
CustomUndoCommand(model),
|
||||
m_model(model),
|
||||
m_row(row),
|
||||
m_count(count)
|
||||
{
|
||||
indexToPath(model, parent, m_parentPath);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Destroys the command.
|
||||
*
|
||||
* Removed entries will be deleted finally.
|
||||
*/
|
||||
EntryModelModifyRowsCommand::~EntryModelModifyRowsCommand()
|
||||
{
|
||||
qDeleteAll(m_values);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Inserts the buffered entries to the model.
|
||||
*/
|
||||
bool EntryModelModifyRowsCommand::insert()
|
||||
{
|
||||
if(Entry *parentEntry = entryFromPathCpy(m_model, m_parentPath)) {
|
||||
if(m_model->insertEntries(m_row, m_model->index(parentEntry), m_values)) {
|
||||
m_values.clear();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Removes the entries from the model.
|
||||
*
|
||||
* The entries and the model have been specified when constructing the class.
|
||||
*
|
||||
* The removed entries are buffered.
|
||||
*/
|
||||
bool EntryModelModifyRowsCommand::remove()
|
||||
{
|
||||
if(Entry *parentEntry = entryFromPathCpy(m_model, m_parentPath)) {
|
||||
m_values = m_model->takeEntries(m_row, m_count, m_model->index(parentEntry));
|
||||
return !m_values.isEmpty();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \class EntryModelInsertRowsCommand
|
||||
* \brief Inserts the specified number of rows before the specified row in the specified entry model under the specified parent.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \brief Constructs a new command.
|
||||
*/
|
||||
EntryModelInsertRowsCommand::EntryModelInsertRowsCommand(EntryModel *model, int row, int count, const QModelIndex &parent) :
|
||||
EntryModelModifyRowsCommand(model, row, count, parent)
|
||||
{
|
||||
setText(QApplication::translate("undocommands", "insertion of %1 entry/entries", 0, count).arg(count));
|
||||
switch(m_model->insertType()) {
|
||||
case EntryType::Account:
|
||||
m_values << new AccountEntry;
|
||||
break;
|
||||
case EntryType::Node:
|
||||
m_values << new NodeEntry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool EntryModelInsertRowsCommand::internalRedo()
|
||||
{
|
||||
return insert();
|
||||
}
|
||||
|
||||
bool EntryModelInsertRowsCommand::internalUndo()
|
||||
{
|
||||
return remove();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \class EntryModelRemoveRowsCommand
|
||||
* \brief Removes the specified number of rows at the specified row in the specified entry model under the specified parent.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \brief Constructs a new command.
|
||||
*/
|
||||
EntryModelRemoveRowsCommand::EntryModelRemoveRowsCommand(EntryModel *model, int row, int count, const QModelIndex &parent) :
|
||||
EntryModelModifyRowsCommand(model, row, count, parent)
|
||||
{
|
||||
setText(QApplication::translate("undocommands", "removal of %1 entry/entries", 0, count).arg(count));
|
||||
}
|
||||
|
||||
bool EntryModelRemoveRowsCommand::internalRedo()
|
||||
{
|
||||
return remove();
|
||||
}
|
||||
|
||||
bool EntryModelRemoveRowsCommand::internalUndo()
|
||||
{
|
||||
return insert();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \class EntryModelMoveRowsCommand
|
||||
* \brief Moves the specified rows to the specified destination within the specified entry model.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \brief Constructs a new command.
|
||||
*/
|
||||
EntryModelMoveRowsCommand::EntryModelMoveRowsCommand(EntryModel *model, const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) :
|
||||
CustomUndoCommand(model),
|
||||
m_model(model),
|
||||
m_sourceRow(sourceRow),
|
||||
m_count(count),
|
||||
m_destChild(destinationChild)
|
||||
{
|
||||
indexToPath(model, sourceParent, m_sourceParentPath);
|
||||
indexToPath(model, destinationParent, m_destParentPath);
|
||||
setText(QApplication::translate("undocommands", "move of %1 entry/entries", 0, count).arg(count));
|
||||
}
|
||||
|
||||
bool EntryModelMoveRowsCommand::internalRedo()
|
||||
{
|
||||
if(m_count) {
|
||||
Entry *sourceParentEntry = entryFromPathCpy(m_model, m_sourceParentPath);
|
||||
Entry *destParentEntry = entryFromPathCpy(m_model, m_destParentPath);
|
||||
if(sourceParentEntry && destParentEntry) {
|
||||
return m_model->moveRows(m_model->index(sourceParentEntry), m_sourceRow, m_count, m_model->index(destParentEntry), m_destChild);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EntryModelMoveRowsCommand::internalUndo()
|
||||
{
|
||||
if(m_count) {
|
||||
Entry *sourceParentEntry = entryFromPathCpy(m_model, m_sourceParentPath);
|
||||
Entry *destParentEntry = entryFromPathCpy(m_model, m_destParentPath);
|
||||
if(sourceParentEntry && destParentEntry) {
|
||||
int sourceRow = m_destChild;
|
||||
int destChild = m_sourceRow;
|
||||
// moves whithin the same parent needs special consideration
|
||||
if(sourceParentEntry == destParentEntry) {
|
||||
// move entry down
|
||||
if(m_sourceRow < m_destChild) {
|
||||
sourceRow -= m_count;
|
||||
// move entry up
|
||||
} else if(m_sourceRow > m_destChild) {
|
||||
destChild += m_count;
|
||||
// keep entry were it is
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return m_model->moveRows(m_model->index(destParentEntry), sourceRow, m_count, m_model->index(sourceParentEntry), destChild);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
#ifndef UNDOCOMMANDS_H
|
||||
#define UNDOCOMMANDS_H
|
||||
|
||||
#include <passwordfile/io/field.h>
|
||||
|
||||
#include <QUndoCommand>
|
||||
#include <QUndoStack>
|
||||
#include <QList>
|
||||
#include <QVariant>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QModelIndex;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Io {
|
||||
class Entry;
|
||||
class AccountEntry;
|
||||
}
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
class FieldModel;
|
||||
class EntryModel;
|
||||
class StackSupport;
|
||||
|
||||
class CustomUndoCommand : public QUndoCommand
|
||||
{
|
||||
public:
|
||||
explicit CustomUndoCommand(StackSupport *stackSupport);
|
||||
bool redoResult() const;
|
||||
bool undoResult() const;
|
||||
bool isNoop() const;
|
||||
void redo();
|
||||
void undo();
|
||||
protected:
|
||||
void setNoop(bool noop);
|
||||
virtual bool internalRedo() = 0;
|
||||
virtual bool internalUndo() = 0;
|
||||
private:
|
||||
StackSupport *m_stackSupport;
|
||||
bool m_redoResult;
|
||||
bool m_undoResult;
|
||||
bool m_noop;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Returns whether the redo action was successful.
|
||||
*/
|
||||
inline bool CustomUndoCommand::redoResult() const
|
||||
{
|
||||
return m_redoResult;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns whether the undo action was successful.
|
||||
*/
|
||||
inline bool CustomUndoCommand::undoResult() const
|
||||
{
|
||||
return m_undoResult;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns whether this command does nothing, eg. the new value equals the old value.
|
||||
*/
|
||||
inline bool CustomUndoCommand::isNoop() const
|
||||
{
|
||||
return m_noop;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Sets whether this command does nothing.
|
||||
*
|
||||
* Meant to be called in the constructor when subclassing and - for example - the new value equals the old value.
|
||||
*/
|
||||
inline void CustomUndoCommand::setNoop(bool noop)
|
||||
{
|
||||
m_noop = noop;
|
||||
}
|
||||
|
||||
class FieldModelSetValueCommand : public CustomUndoCommand
|
||||
{
|
||||
public:
|
||||
explicit FieldModelSetValueCommand(FieldModel *model, const QModelIndex &index, const QVariant &value, int role);
|
||||
protected:
|
||||
bool internalRedo();
|
||||
bool internalUndo();
|
||||
private:
|
||||
Io::AccountEntry *m_account;
|
||||
FieldModel *m_model;
|
||||
int m_row;
|
||||
int m_col;
|
||||
QVariant m_newValue;
|
||||
QVariant m_oldValue;
|
||||
int m_role;
|
||||
};
|
||||
|
||||
class FieldModelInsertRowsCommand : public CustomUndoCommand
|
||||
{
|
||||
public:
|
||||
explicit FieldModelInsertRowsCommand(FieldModel *model, int row, int count);
|
||||
protected:
|
||||
bool internalRedo();
|
||||
bool internalUndo();
|
||||
private:
|
||||
Io::AccountEntry *m_account;
|
||||
FieldModel *m_model;
|
||||
int m_row;
|
||||
int m_count;
|
||||
};
|
||||
|
||||
class FieldModelRemoveRowsCommand : public CustomUndoCommand
|
||||
{
|
||||
public:
|
||||
explicit FieldModelRemoveRowsCommand(FieldModel *model, int row, int count);
|
||||
protected:
|
||||
bool internalRedo();
|
||||
bool internalUndo();
|
||||
private:
|
||||
Io::AccountEntry *m_account;
|
||||
FieldModel *m_model;
|
||||
int m_row;
|
||||
int m_count;
|
||||
QList<Io::Field> m_values;
|
||||
};
|
||||
|
||||
class EntryModelSetValueCommand : public CustomUndoCommand
|
||||
{
|
||||
public:
|
||||
explicit EntryModelSetValueCommand(EntryModel *model, const QModelIndex &index, const QVariant &value, int role);
|
||||
protected:
|
||||
bool internalRedo();
|
||||
bool internalUndo();
|
||||
private:
|
||||
EntryModel *m_model;
|
||||
std::list<std::string> m_path;
|
||||
QVariant m_newValue;
|
||||
QVariant m_oldValue;
|
||||
int m_role;
|
||||
};
|
||||
|
||||
class EntryModelModifyRowsCommand : public CustomUndoCommand
|
||||
{
|
||||
public:
|
||||
~EntryModelModifyRowsCommand();
|
||||
protected:
|
||||
explicit EntryModelModifyRowsCommand(EntryModel *model, int row, int count, const QModelIndex &parent);
|
||||
bool internalRedo() = 0;
|
||||
bool internalUndo() = 0;
|
||||
bool insert();
|
||||
bool remove();
|
||||
EntryModel *m_model;
|
||||
std::list<std::string> m_parentPath;
|
||||
int m_row;
|
||||
int m_count;
|
||||
QList<Io::Entry *> m_values;
|
||||
};
|
||||
|
||||
class EntryModelInsertRowsCommand : public EntryModelModifyRowsCommand
|
||||
{
|
||||
public:
|
||||
explicit EntryModelInsertRowsCommand(EntryModel *model, int row, int count, const QModelIndex &parent);
|
||||
protected:
|
||||
bool internalRedo();
|
||||
bool internalUndo();
|
||||
};
|
||||
|
||||
class EntryModelRemoveRowsCommand : public EntryModelModifyRowsCommand
|
||||
{
|
||||
public:
|
||||
explicit EntryModelRemoveRowsCommand(EntryModel *model, int row, int count, const QModelIndex &parent);
|
||||
protected:
|
||||
bool internalRedo();
|
||||
bool internalUndo();
|
||||
};
|
||||
|
||||
class EntryModelMoveRowsCommand : public CustomUndoCommand
|
||||
{
|
||||
public:
|
||||
explicit EntryModelMoveRowsCommand(EntryModel *model, const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild);
|
||||
protected:
|
||||
bool internalRedo();
|
||||
bool internalUndo();
|
||||
private:
|
||||
EntryModel *m_model;
|
||||
std::list<std::string> m_sourceParentPath;
|
||||
int m_sourceRow;
|
||||
int m_count;
|
||||
std::list<std::string> m_destParentPath;
|
||||
int m_destChild;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // UNDOCOMMANDS_H
|
|
@ -0,0 +1,95 @@
|
|||
#include "cli/cli.h"
|
||||
#ifdef GUI_QTWIDGETS
|
||||
# include "gui/initiate.h"
|
||||
#endif
|
||||
#ifdef GUI_QTQUICK
|
||||
# include "quickgui/initiate.h"
|
||||
#endif
|
||||
|
||||
#include <passwordfile/util/openssl.h>
|
||||
|
||||
#include <c++utilities/application/argumentparser.h>
|
||||
#include <c++utilities/application/failure.h>
|
||||
|
||||
#if defined(GUI_QTWIDGETS) || defined(GUI_QTQUICK)
|
||||
# include <qtutilities/resources/qtconfigarguments.h>
|
||||
#else
|
||||
# include <c++utilities/application/fakeqtconfigarguments.h>
|
||||
#endif
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace ApplicationUtilities;
|
||||
using namespace Util;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// init open ssl
|
||||
OpenSsl::init();
|
||||
// setup argument parser
|
||||
ArgumentParser parser;
|
||||
parser.setIgnoreUnknownArguments(true);
|
||||
// Qt configuration arguments
|
||||
QT_CONFIG_ARGUMENTS qtConfigArgs;
|
||||
// file argument
|
||||
Argument fileArg("file", "f", "specifies the file to be opened (or created when using --modify)");
|
||||
fileArg.setValueNames({"path"});
|
||||
fileArg.setRequiredValueCount(1);
|
||||
fileArg.setCombinable(true);
|
||||
fileArg.setRequired(false);
|
||||
fileArg.setImplicit(true);
|
||||
// cli argument
|
||||
Argument cliArg("interactive-cli", "i", "starts the interactive command line interface");
|
||||
// help argument
|
||||
HelpArgument helpArg(parser);
|
||||
parser.setMainArguments({&fileArg, &helpArg, &(qtConfigArgs.qtWidgetsGuiArg()), &(qtConfigArgs.qtQuickGuiArg()), &cliArg});
|
||||
// holds the application's return code
|
||||
int res = 0;
|
||||
// parse the specified arguments
|
||||
try {
|
||||
parser.parseArgs(argc, argv);
|
||||
if(cliArg.isPresent()) {
|
||||
Cli::InteractiveCli cli;
|
||||
if(fileArg.isPresent() && fileArg.valueCount() == 1) {
|
||||
cli.run(fileArg.value(0));
|
||||
} else {
|
||||
cli.run();
|
||||
}
|
||||
} else if(qtConfigArgs.areQtGuiArgsPresent()) {
|
||||
// run Qt gui if no arguments, --qt-gui or --qt-quick-gui specified, a file might be specified
|
||||
QString file;
|
||||
if(fileArg.valueCount() > 0) {
|
||||
file = QString::fromLocal8Bit(fileArg.value(0).c_str());
|
||||
}
|
||||
if(qtConfigArgs.qtWidgetsGuiArg().isPresent()) {
|
||||
#ifdef GUI_QTWIDGETS
|
||||
res = QtGui::runWidgetsGui(argc, argv, file);
|
||||
#else
|
||||
cout << "The application has not been built with Qt widgets support." << endl;
|
||||
#endif
|
||||
} else if(qtConfigArgs.qtQuickGuiArg().isPresent()) {
|
||||
#ifdef GUI_QTQUICK
|
||||
res = QtGui::runQuickGui(argc, argv);
|
||||
#else
|
||||
cout << "The application has not been built with Qt quick support." << endl;
|
||||
#endif
|
||||
} else {
|
||||
#if defined(GUI_QTQUICK)
|
||||
res = QtGui::runQuickGui(argc, argv);
|
||||
#elif defined(GUI_QTWIDGETS)
|
||||
res = QtGui::runWidgetsGui(argc, argv, file);
|
||||
#else
|
||||
cout << "See --help for usage." << endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} catch(Failure &ex) {
|
||||
cout << "Unable to parse arguments. " << ex.what() << "\nSee --help for available commands." << endl;
|
||||
}
|
||||
// clean open ssl
|
||||
OpenSsl::clean();
|
||||
return res;
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
#include "entryfiltermodel.h"
|
||||
#include "entrymodel.h"
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
/*!
|
||||
* \class EntryFilterModel
|
||||
* \brief The EntryFilterModel class provides a QSortFilterProxyModel specialization for the EntryModel class.
|
||||
*
|
||||
* \sa http://qt-project.org/doc/qt-5/qsortfilterproxymodel.html
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \brief Constructs a new filter entry model.
|
||||
*/
|
||||
EntryFilterModel::EntryFilterModel(QObject *parent) :
|
||||
QSortFilterProxyModel(parent)
|
||||
{}
|
||||
|
||||
bool EntryFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
||||
{
|
||||
if(QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent)) {
|
||||
return true;
|
||||
}
|
||||
QModelIndex sourceIndex = sourceModel()->index(sourceRow, 0, sourceParent);
|
||||
if(sourceModel()->hasChildren(sourceIndex)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef ENTRYFILTERMODEL_H
|
||||
#define ENTRYFILTERMODEL_H
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
class EntryFilterModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit EntryFilterModel(QObject *parent = nullptr);
|
||||
|
||||
protected:
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // ENTRYFILTERMODEL_H
|
|
@ -0,0 +1,563 @@
|
|||
#include "entrymodel.h"
|
||||
|
||||
#ifdef MODEL_UNDO_SUPPORT
|
||||
#include "gui/undocommands.h"
|
||||
#endif
|
||||
|
||||
#include <passwordfile/io/entry.h>
|
||||
|
||||
#include <QIcon>
|
||||
#include <QBuffer>
|
||||
#include <QMimeData>
|
||||
#include <QDebug>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
using namespace Io;
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
|
||||
/*!
|
||||
* \class EntryModel
|
||||
* \brief The EntryModel class provides a model interface for a hierarchy of Entry instances.
|
||||
*
|
||||
* If MODEL_UNDO_SUPPORT the model supports Qt's undo framework.
|
||||
* \sa http://qt-project.org/doc/qt-5/qabstractitemmodel.html
|
||||
* \sa http://qt-project.org/doc/qt-5/qundo.html
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \brief Constructs a new entry model.
|
||||
*/
|
||||
EntryModel::EntryModel(QObject *parent) :
|
||||
QAbstractItemModel(parent),
|
||||
m_rootEntry(nullptr),
|
||||
m_insertType(EntryType::Node)
|
||||
{}
|
||||
|
||||
#ifdef MODEL_UNDO_SUPPORT
|
||||
/*!
|
||||
* \brief Constructs a new entry model with the specified \a undoStack.
|
||||
*
|
||||
* This constructor is only available when MODEL_UNDO_SUPPORT is defined.
|
||||
*/
|
||||
EntryModel::EntryModel(QUndoStack *undoStack, QObject *parent) :
|
||||
QAbstractItemModel(parent),
|
||||
StackSupport(undoStack),
|
||||
m_rootEntry(nullptr),
|
||||
m_insertType(EntryType::Node)
|
||||
{}
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* \brief Returns the Entry for the specified \a index of nullptr if the \a index is invalid.
|
||||
*
|
||||
* Modifications should be done using the common methods defined by the QAbstractItemModel class.
|
||||
* This method is intended to be used only internally to implement the QAbstractItemModel interface
|
||||
* or when implementing a QUndoCommand to support Qt's undo framework.
|
||||
*/
|
||||
Entry *EntryModel::entry(const QModelIndex &index)
|
||||
{
|
||||
if(index.isValid()) {
|
||||
return static_cast<Entry *>(index.internalPointer());
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Removes \a count number of entries at the specified \a row within the specified \a parent.
|
||||
* \returns Returns the removed entries (rather then deleting them).
|
||||
*
|
||||
* Modifications should be done using the common methods defined by the QAbstractItemModel class.
|
||||
* This method is intended to be used only internally to implement the QAbstractItemModel interface
|
||||
* or when implementing a QUndoCommand to support Qt's undo framework.
|
||||
*/
|
||||
QList<Entry *> EntryModel::takeEntries(int row, int count, const QModelIndex &parent)
|
||||
{
|
||||
QList<Entry *> res;
|
||||
if(Entry *parentEntry = entry(parent)) {
|
||||
if(parentEntry->type() == EntryType::Node) {
|
||||
NodeEntry *parentNodeEntry = static_cast<NodeEntry *>(parentEntry);
|
||||
int lastIndex = row + count - 1;
|
||||
const vector<Entry *> &children = parentNodeEntry->children();
|
||||
if(lastIndex < 0 || static_cast<size_t>(lastIndex) >= children.size()) {
|
||||
lastIndex = children.size() - 1;
|
||||
}
|
||||
beginRemoveRows(parent, row, lastIndex);
|
||||
for(int index = lastIndex; index >= row; --index) {
|
||||
Entry *child = children.at(index);
|
||||
child->setParent(nullptr);
|
||||
res << child;
|
||||
}
|
||||
endRemoveRows();
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Inserts the specified \a entries before the specified \a row within the specified \a parent.
|
||||
*
|
||||
* Modifications should be done using the common methods defined by the QAbstractItemModel class.
|
||||
* This method is intended to be used only internally to implement the QAbstractItemModel interface
|
||||
* or when implementing a QUndoCommand to support Qt's undo framework.
|
||||
*/
|
||||
bool EntryModel::insertEntries(int row, const QModelIndex &parent, const QList<Entry *> &entries)
|
||||
{
|
||||
if(entries.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
if(Entry *parentEntry = entry(parent)) {
|
||||
if(parentEntry->type() == EntryType::Node) {
|
||||
NodeEntry *parentNodeEntry = static_cast<NodeEntry *>(parentEntry);
|
||||
const vector<Entry *> &children = parentNodeEntry->children();
|
||||
if(row < 0 || static_cast<size_t>(row) > children.size()) {
|
||||
row = children.size();
|
||||
}
|
||||
beginInsertRows(parent, row, row + entries.size() - 1);
|
||||
foreach(Entry *entry, entries) {
|
||||
entry->setParent(parentNodeEntry, row);
|
||||
++row;
|
||||
}
|
||||
endInsertRows();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QModelIndex EntryModel::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
if(parent.isValid()) {
|
||||
if(Entry *parentEntry = static_cast<Entry *>(parent.internalPointer())) {
|
||||
switch(parentEntry->type()) {
|
||||
case EntryType::Node: {
|
||||
const std::vector<Entry *> &children = static_cast<NodeEntry *>(parentEntry)->children();
|
||||
if(row >= 0 && static_cast<size_t>(row) < children.size()) {
|
||||
return createIndex(row, column, children.at(row));
|
||||
}
|
||||
break;
|
||||
} case EntryType::Account:
|
||||
;
|
||||
}
|
||||
}
|
||||
} else if(m_rootEntry && row == 0) {
|
||||
return createIndex(row, column, m_rootEntry);
|
||||
}
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the index of the specified \a entry.
|
||||
* \remarks It is up to the caller to ensure that \a entry is a child of the root element.
|
||||
*/
|
||||
QModelIndex EntryModel::index(Entry *entry) const
|
||||
{
|
||||
if(entry->parent()) {
|
||||
return createIndex(entry->index(), 0, entry);
|
||||
} else {
|
||||
return createIndex(0, 0, m_rootEntry);
|
||||
}
|
||||
}
|
||||
|
||||
QModelIndex EntryModel::parent(const QModelIndex &child) const
|
||||
{
|
||||
if(child.isValid()) {
|
||||
if(Entry *entry = static_cast<Entry *>(child.internalPointer())) {
|
||||
NodeEntry *parent = entry->parent();
|
||||
if(parent && (child.row() >= 0 && static_cast<size_t>(child.row()) < parent->children().size())) {
|
||||
return createIndex(parent->index() > 0 ? parent->index() : 0, 0, parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
bool EntryModel::hasChildren(const QModelIndex &parent) const
|
||||
{
|
||||
if(parent.isValid()) {
|
||||
if(Entry *entry = static_cast<Entry *>(parent.internalPointer())) {
|
||||
if(entry->type() == EntryType::Node) {
|
||||
return static_cast<NodeEntry *>(entry)->children().size();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns an indication whether the specified \a parent might have children.
|
||||
* \remarks Only node entries might have childs.
|
||||
*/
|
||||
bool EntryModel::isNode(const QModelIndex &parent) const
|
||||
{
|
||||
if(parent.isValid()) {
|
||||
if(Entry *entry = static_cast<Entry *>(parent.internalPointer())) {
|
||||
return entry->type() == EntryType::Node;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariant EntryModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if(index.isValid()) {
|
||||
if(Entry *entry = static_cast<Entry *>(index.internalPointer())) {
|
||||
switch(role) {
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
switch(index.column()) {
|
||||
case 0:
|
||||
return QString::fromStdString(entry->label());
|
||||
default:
|
||||
;
|
||||
}
|
||||
break;
|
||||
case Qt::DecorationRole:
|
||||
if(index.column() == 0 && entry->type() == EntryType::Node) {
|
||||
static const QVariant folderIcon = QIcon::fromTheme(QStringLiteral("folder"));
|
||||
return folderIcon;
|
||||
}
|
||||
break;
|
||||
case SerializedRole: {
|
||||
stringstream ss(stringstream::in | stringstream::out | stringstream::binary);
|
||||
ss.exceptions(std::stringstream::failbit | std::stringstream::badbit);
|
||||
try {
|
||||
entry->make(ss);
|
||||
string string = ss.str();
|
||||
return QByteArray(string.data(), string.size());
|
||||
} catch(const ios_base::failure &) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DefaultExpandedRole:
|
||||
return entry->type() == EntryType::Node && static_cast<NodeEntry *>(entry)->isExpandedByDefault();
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QMap<int, QVariant> EntryModel::itemData(const QModelIndex &index) const
|
||||
{
|
||||
QMap<int, QVariant> roles;
|
||||
roles.insert(Qt::DisplayRole, data(index, Qt::DisplayRole));
|
||||
roles.insert(SerializedRole, data(index, SerializedRole));
|
||||
return roles;
|
||||
}
|
||||
|
||||
bool EntryModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
#ifdef MODEL_UNDO_SUPPORT
|
||||
if(undoStack()) {
|
||||
return push(new EntryModelSetValueCommand(this, index, value, role));
|
||||
}
|
||||
#endif
|
||||
if(index.isValid()) {
|
||||
if(Entry *entry = static_cast<Entry *>(index.internalPointer())) {
|
||||
switch(role) {
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
switch(index.column()) {
|
||||
case 0:
|
||||
entry->setLabel(value.toString().toStdString());
|
||||
emit dataChanged(index, index, QVector<int>() << role);
|
||||
return true;
|
||||
default:
|
||||
;
|
||||
}
|
||||
break;
|
||||
case SerializedRole: {
|
||||
NodeEntry *parent = entry->parent();
|
||||
QModelIndex parentIndex = index.parent();
|
||||
if(parent && parentIndex.isValid()) {
|
||||
stringstream ss(stringstream::in | stringstream::out | stringstream::binary);
|
||||
ss.exceptions(std::stringstream::failbit | std::stringstream::badbit);
|
||||
QByteArray array = value.toByteArray();
|
||||
if(array.size()) {
|
||||
try {
|
||||
ss.write(array.data(), array.size());
|
||||
Entry *newEntry = Entry::parse(ss);
|
||||
int row = entry->index();
|
||||
beginRemoveRows(parentIndex, row, row);
|
||||
delete entry;
|
||||
endRemoveRows();
|
||||
beginInsertRows(parentIndex, row, row);
|
||||
newEntry->setParent(parent, row);
|
||||
endInsertRows();
|
||||
return true;
|
||||
} catch(const ios_base::failure &) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DefaultExpandedRole:
|
||||
switch(entry->type()) {
|
||||
case EntryType::Account:
|
||||
return false;
|
||||
case EntryType::Node:
|
||||
static_cast<NodeEntry *>(entry)->setExpandedByDefault(value.toBool());
|
||||
emit dataChanged(index, index, QVector<int>() << role);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EntryModel::setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles)
|
||||
{
|
||||
for(QMap<int, QVariant>::ConstIterator it = roles.constBegin(); it != roles.constEnd(); ++it) {
|
||||
setData(index, it.value(), it.key());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Qt::ItemFlags EntryModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
if(isNode(index)) {
|
||||
return QAbstractItemModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
|
||||
} else {
|
||||
return QAbstractItemModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
QVariant EntryModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
switch(orientation) {
|
||||
case Qt::Horizontal:
|
||||
switch(role) {
|
||||
case Qt::DisplayRole:
|
||||
switch(section) {
|
||||
case 0:
|
||||
return tr("Name");
|
||||
default:
|
||||
;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
return QVariant();
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
int EntryModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
if(parent.isValid()) {
|
||||
if(Entry *parentEntry = static_cast<Entry *>(parent.internalPointer())) {
|
||||
switch(parentEntry->type()) {
|
||||
case EntryType::Node:
|
||||
return static_cast<NodeEntry *>(parentEntry)->children().size();
|
||||
case EntryType::Account:
|
||||
;
|
||||
}
|
||||
}
|
||||
} else if(m_rootEntry) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int EntryModel::columnCount(const QModelIndex &) const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool EntryModel::insertRows(int row, int count, const QModelIndex &parent)
|
||||
{
|
||||
#ifdef MODEL_UNDO_SUPPORT
|
||||
if(undoStack()) {
|
||||
return push(new EntryModelInsertRowsCommand(this, row, count, parent));
|
||||
}
|
||||
#endif
|
||||
if(parent.isValid()) {
|
||||
if(Entry *parentEntry = static_cast<Entry *>(parent.internalPointer())) {
|
||||
if(parentEntry->type() == EntryType::Node) {
|
||||
beginInsertRows(parent, row, row + count - 1);
|
||||
for(int end = row + count; row < end; ++row) {
|
||||
Entry *newEntry;
|
||||
switch(m_insertType) {
|
||||
case EntryType::Node:
|
||||
newEntry = new NodeEntry;
|
||||
break;
|
||||
case EntryType::Account:
|
||||
newEntry = new AccountEntry;
|
||||
break;
|
||||
default:
|
||||
return false; // should never be reached, just to suppress compiler warning
|
||||
}
|
||||
newEntry->setParent(static_cast<NodeEntry *>(parentEntry), row);
|
||||
}
|
||||
endInsertRows();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EntryModel::removeRows(int row, int count, const QModelIndex &parent)
|
||||
{
|
||||
#ifdef MODEL_UNDO_SUPPORT
|
||||
if(undoStack()) {
|
||||
return push(new EntryModelRemoveRowsCommand(this, row, count, parent));
|
||||
}
|
||||
#endif
|
||||
if(parent.isValid() && count > 0) {
|
||||
if(Entry *parentEntry = static_cast<Entry *>(parent.internalPointer())) {
|
||||
if(parentEntry->type() == EntryType::Node) {
|
||||
beginRemoveRows(parent, row, row + count - 1);
|
||||
static_cast<NodeEntry *>(parentEntry)->deleteChildren(row, row + count);
|
||||
endRemoveRows();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EntryModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild)
|
||||
{
|
||||
#ifdef MODEL_UNDO_SUPPORT
|
||||
if(undoStack()) {
|
||||
return push(new EntryModelMoveRowsCommand(this, sourceParent, sourceRow, count, destinationParent, destinationChild));
|
||||
}
|
||||
#endif
|
||||
// check validation of specified arguments
|
||||
if(sourceParent.isValid() && destinationParent.isValid()
|
||||
&& sourceRow >= 0 && count > 0
|
||||
&& entry(sourceParent)->type() == EntryType::Node // source and destination parent entries
|
||||
&& entry(destinationParent)->type() == EntryType::Node) { // need to be node entries
|
||||
// determine the source parent entry and dest parent entry as node entries
|
||||
NodeEntry *srcParentEntry = static_cast<NodeEntry *>(sourceParent.internalPointer());
|
||||
NodeEntry *destParentEntry = static_cast<NodeEntry *>(destinationParent.internalPointer());
|
||||
// source rows must be within the valid range
|
||||
if(static_cast<size_t>(sourceRow + count) <= srcParentEntry->children().size()
|
||||
// if source and destination parent are the same the destination child mustn't be in the source range
|
||||
&& (srcParentEntry != destParentEntry || (destinationChild < sourceRow || (sourceRow + count) < destinationChild))) {
|
||||
// do not move a row to one of its own children! -> check before
|
||||
for(int index = 0; index < count; ++index) {
|
||||
Entry *toMove = srcParentEntry->children().at(sourceRow + index);
|
||||
if(toMove->type() == EntryType::Node) {
|
||||
if(destParentEntry->isIndirectChildOf(static_cast<NodeEntry *>(toMove))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// actually perform the move operation
|
||||
beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destinationParent, destinationChild);
|
||||
for(int index = 0; index < count; ++index) {
|
||||
Entry *toMove = srcParentEntry->children().at(sourceRow + index);
|
||||
if(srcParentEntry == destParentEntry && sourceRow < destinationChild) {
|
||||
toMove->setParent(destParentEntry, destinationChild + index - 1);
|
||||
} else {
|
||||
toMove->setParent(destParentEntry, destinationChild + index);
|
||||
}
|
||||
}
|
||||
endMoveRows();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList EntryModel::mimeTypes() const
|
||||
{
|
||||
return QStringList() << QStringLiteral("application/x-entrymodelpathlistmove") << QStringLiteral("text/plain");
|
||||
}
|
||||
|
||||
QMimeData *EntryModel::mimeData(const QModelIndexList &indexes) const
|
||||
{
|
||||
if(indexes.count() <= 0) {
|
||||
return nullptr;
|
||||
}
|
||||
QStringList types = mimeTypes();
|
||||
if(types.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
QMimeData *data = new QMimeData();
|
||||
QStringList plainTextParts;
|
||||
QByteArray encoded;
|
||||
QDataStream dataStream(&encoded, QIODevice::WriteOnly);
|
||||
foreach(const QModelIndex &index, indexes) {
|
||||
if(index.isValid()) {
|
||||
Entry *entry = static_cast<Entry *>(index.internalPointer());
|
||||
list<string> path = entry->path();
|
||||
dataStream << static_cast<quint32>(path.size());
|
||||
for(const string &part : path) {
|
||||
dataStream << QString::fromStdString(part);
|
||||
}
|
||||
plainTextParts << QString::fromStdString(entry->label());
|
||||
}
|
||||
}
|
||||
data->setData(types.at(0), encoded);
|
||||
data->setText(plainTextParts.join(QStringLiteral(", ")));
|
||||
return data;
|
||||
}
|
||||
|
||||
bool EntryModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
|
||||
{
|
||||
if(!m_rootEntry || !data || action != Qt::MoveAction) {
|
||||
return false;
|
||||
}
|
||||
QStringList types = mimeTypes();
|
||||
if(types.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
QString format = types.at(0);
|
||||
if(!data->hasFormat(format)) {
|
||||
return false;
|
||||
}
|
||||
if(row > rowCount(parent) || row < 0) {
|
||||
row = rowCount(parent);
|
||||
}
|
||||
if(column > columnCount(parent) || column < 0) {
|
||||
column = 0;
|
||||
}
|
||||
// decode and insert
|
||||
QByteArray encoded = data->data(format);
|
||||
QDataStream stream(&encoded, QIODevice::ReadOnly);
|
||||
int moved = 0;
|
||||
while(!stream.atEnd()) {
|
||||
quint32 size;
|
||||
stream >> size;
|
||||
list<string> path;
|
||||
for(quint32 i = 0; i < size; ++i) {
|
||||
QString part;
|
||||
stream >> part;
|
||||
path.push_back(part.toStdString());
|
||||
}
|
||||
if(Entry *entry = m_rootEntry->entryByPath(path, true)) {
|
||||
if(NodeEntry *srcParentEntry = entry->parent()) {
|
||||
if(moveRows(index(srcParentEntry), entry->index(), 1, parent, row)) {
|
||||
++moved;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Qt::DropActions EntryModel::supportedDropActions() const
|
||||
{
|
||||
return Qt::MoveAction;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
#ifndef ENTRYMODEL_H
|
||||
#define ENTRYMODEL_H
|
||||
|
||||
#ifdef MODEL_UNDO_SUPPORT
|
||||
#include "gui/stacksupport.h"
|
||||
#endif
|
||||
|
||||
#include <c++utilities/application/global.h>
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
|
||||
namespace Io {
|
||||
class Entry;
|
||||
class NodeEntry;
|
||||
DECLARE_ENUM(EntryType, int)
|
||||
}
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
/*!
|
||||
* \brief The EntryModelRoles enum defines custom roles for the EntryModel class.
|
||||
*/
|
||||
enum EntryModelRoles
|
||||
{
|
||||
SerializedRole = Qt::UserRole + 1, /**< the entry (including descendants) in serialized from (QByteArray) */
|
||||
DefaultExpandedRole = Qt::UserRole + 2 /**< whether the entry should be expanded by default */
|
||||
};
|
||||
|
||||
class EntryModel : public QAbstractItemModel
|
||||
#ifdef MODEL_UNDO_SUPPORT
|
||||
, public StackSupport
|
||||
#endif
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit EntryModel(QObject *parent = nullptr);
|
||||
#ifdef MODEL_UNDO_SUPPORT
|
||||
explicit EntryModel(QUndoStack *undoStack, QObject *parent = nullptr);
|
||||
#endif
|
||||
|
||||
Io::NodeEntry *rootEntry();
|
||||
void setRootEntry(Io::NodeEntry *entry);
|
||||
Io::Entry *entry(const QModelIndex &index);
|
||||
QList<Io::Entry *> takeEntries(int row, int count, const QModelIndex &parent);
|
||||
bool insertEntries(int row, const QModelIndex &parent, const QList<Io::Entry *> &entries);
|
||||
Io::EntryType insertType() const;
|
||||
void setInsertType(Io::EntryType type);
|
||||
bool hidePasswords() const;
|
||||
QModelIndex index(int row, int column, const QModelIndex &parent) const;
|
||||
QModelIndex index(Io::Entry *entry) const;
|
||||
QModelIndex parent(const QModelIndex &child) const;
|
||||
bool hasChildren(const QModelIndex &parent) const;
|
||||
bool isNode(const QModelIndex &parent) const;
|
||||
QVariant data(const QModelIndex &index, int role) const;
|
||||
QMap<int, QVariant> itemData(const QModelIndex &index) const;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role);
|
||||
bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles);
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
|
||||
int rowCount(const QModelIndex &parent) const;
|
||||
int columnCount(const QModelIndex &parent) const;
|
||||
bool insertRows(int row, int count, const QModelIndex &parent);
|
||||
bool removeRows(int row, int count, const QModelIndex &parent);
|
||||
bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild);
|
||||
QStringList mimeTypes() const;
|
||||
QMimeData *mimeData(const QModelIndexList &indexes) const;
|
||||
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
|
||||
Qt::DropActions supportedDropActions() const;
|
||||
|
||||
public slots:
|
||||
void reset();
|
||||
|
||||
private:
|
||||
Io::NodeEntry *m_rootEntry;
|
||||
Io::EntryType m_insertType;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Returns the root entry.
|
||||
*/
|
||||
inline Io::NodeEntry *EntryModel::rootEntry()
|
||||
{
|
||||
return m_rootEntry;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Sets the root entry. Causes a model reset. The undo stack will be cleard if MODEL_UNDO_SUPPORT is defined.
|
||||
*/
|
||||
inline void EntryModel::setRootEntry(Io::NodeEntry *entry)
|
||||
{
|
||||
if(m_rootEntry != entry) {
|
||||
#ifdef MODEL_UNDO_SUPPORT
|
||||
clearUndoStack();
|
||||
#endif
|
||||
beginResetModel();
|
||||
m_rootEntry = entry;
|
||||
endResetModel();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Resets the model. The root entry will be unset.
|
||||
*/
|
||||
inline void EntryModel::reset()
|
||||
{
|
||||
setRootEntry(nullptr);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the entry type used when inserting new rows.
|
||||
*/
|
||||
inline Io::EntryType EntryModel::insertType() const
|
||||
{
|
||||
return m_insertType;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Sets the entry type used when inserting new rows.
|
||||
*/
|
||||
inline void EntryModel::setInsertType(Io::EntryType type)
|
||||
{
|
||||
m_insertType = type;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // ENTRYMODEL_H
|
|
@ -0,0 +1,339 @@
|
|||
#include "fieldmodel.h"
|
||||
#ifdef MODEL_UNDO_SUPPORT
|
||||
#include "gui/undocommands.h"
|
||||
#endif
|
||||
|
||||
#include <passwordfile/io/field.h>
|
||||
|
||||
#include <QMimeData>
|
||||
#include <QStringList>
|
||||
|
||||
using namespace std;
|
||||
using namespace Io;
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
/*!
|
||||
* \class FieldModel
|
||||
* \brief The FieldModel class provides a model interface for the fields of an AccountEntry.
|
||||
*
|
||||
* If MODEL_UNDO_SUPPORT the model supports Qt's undo framework.
|
||||
* \sa http://qt-project.org/doc/qt-5/qabstracttablemodel.html
|
||||
* \sa http://qt-project.org/doc/qt-5/qundo.html
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \brief Constructs a new field model.
|
||||
*/
|
||||
FieldModel::FieldModel(QObject *parent) :
|
||||
QAbstractTableModel(parent),
|
||||
m_accountEntry(nullptr),
|
||||
m_fields(nullptr),
|
||||
m_hidePasswords(false)
|
||||
{}
|
||||
|
||||
#ifdef MODEL_UNDO_SUPPORT
|
||||
/*!
|
||||
* \brief Constructs a new field model with the specified \a undoStack.
|
||||
*
|
||||
* This constructor is only available when MODEL_UNDO_SUPPORT is defined.
|
||||
*/
|
||||
FieldModel::FieldModel(QUndoStack *undoStack, QObject *parent) :
|
||||
QAbstractTableModel(parent),
|
||||
StackSupport(undoStack),
|
||||
m_accountEntry(nullptr),
|
||||
m_fields(nullptr)
|
||||
{}
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* \brief Sets the account entry. Causes a model reset.
|
||||
*
|
||||
* The \a entry might be nullptr.
|
||||
* The caller keeps the ownership and should not destroy the \a entry as long it is assigned.
|
||||
*/
|
||||
void FieldModel::setAccountEntry(AccountEntry *entry)
|
||||
{
|
||||
if(entry != m_accountEntry) {
|
||||
beginResetModel();
|
||||
if((m_accountEntry = entry)) {
|
||||
m_fields = &entry->fields();
|
||||
} else {
|
||||
m_fields = nullptr;
|
||||
}
|
||||
endResetModel();
|
||||
}
|
||||
}
|
||||
|
||||
QVariant FieldModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if(index.isValid() && m_fields && index.row() >= 0) {
|
||||
// return data for existent field
|
||||
if(static_cast<size_t>(index.row()) < m_fields->size()) {
|
||||
switch(role) {
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
switch(index.column()) {
|
||||
case 0:
|
||||
return QString::fromStdString(m_fields->at(index.row()).name());
|
||||
case 1:
|
||||
if(role == Qt::DisplayRole
|
||||
&& m_hidePasswords
|
||||
&& m_fields->at(index.row()).type() == FieldType::Password) {
|
||||
return QString(m_fields->at(index.row()).value().size(), QChar(0x2022));
|
||||
} else {
|
||||
return QString::fromStdString(m_fields->at(index.row()).value());
|
||||
}
|
||||
default:
|
||||
;
|
||||
}
|
||||
break;
|
||||
case FieldTypeRole:
|
||||
return static_cast<int>(m_fields->at(index.row()).type());
|
||||
default:
|
||||
;
|
||||
}
|
||||
// return data for empty field at the end which enables the user to append fields
|
||||
} else if(static_cast<size_t>(index.row()) == m_fields->size()) {
|
||||
switch(role) {
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
switch(index.column()) {
|
||||
case 0:
|
||||
return QString();
|
||||
case 1:
|
||||
return QString();
|
||||
default:
|
||||
;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QMap<int, QVariant> FieldModel::itemData(const QModelIndex &index) const
|
||||
{
|
||||
static int roles[] = {Qt::EditRole, FieldTypeRole};
|
||||
QMap<int, QVariant> roleMap;
|
||||
for(int role : roles) {
|
||||
QVariant variantData = data(index, role);
|
||||
if (variantData.isValid()) {
|
||||
roleMap.insert(role, variantData);
|
||||
}
|
||||
}
|
||||
return roleMap;
|
||||
}
|
||||
|
||||
bool FieldModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
#if MODEL_UNDO_SUPPORT
|
||||
if(undoStack()) {
|
||||
return push(new FieldModelSetValueCommand(this, index, value, role));
|
||||
}
|
||||
#endif
|
||||
QVector<int> roles;
|
||||
if(index.isValid() && m_fields && index.row() >= 0) {
|
||||
// set data for existing field
|
||||
if(static_cast<size_t>(index.row()) < m_fields->size()) {
|
||||
switch(role) {
|
||||
case Qt::EditRole:
|
||||
switch(index.column()) {
|
||||
case 0:
|
||||
m_fields->at(index.row()).setName(value.toString().toStdString());
|
||||
roles << role;
|
||||
break;
|
||||
case 1:
|
||||
m_fields->at(index.row()).setValue(value.toString().toStdString());
|
||||
roles << role;
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
break;
|
||||
case FieldTypeRole: {
|
||||
bool ok;
|
||||
int fieldType = value.toInt(&ok);
|
||||
if(ok && Field::isValidType(fieldType)) {
|
||||
roles << role;
|
||||
m_fields->at(index.row()).setType(static_cast<FieldType>(fieldType));
|
||||
}
|
||||
break;
|
||||
} default:
|
||||
;
|
||||
}
|
||||
// remove last field if empty, showing an empty field at the end to enabled appending new rows is provided by the data method
|
||||
if(!roles.isEmpty() && static_cast<size_t>(index.row()) == m_fields->size() - 1 && m_fields->at(index.row()).isEmpty()) {
|
||||
beginRemoveRows(index.parent(), index.row(), index.row());
|
||||
m_fields->pop_back();
|
||||
endRemoveRows();
|
||||
}
|
||||
// set data for a new field emplaced at the end of the field list
|
||||
} else if(static_cast<size_t>(index.row()) == m_fields->size() && !value.toString().isEmpty()) {
|
||||
switch(role) {
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
switch(index.column()) {
|
||||
case 0:
|
||||
beginInsertRows(index.parent(), rowCount(), rowCount());
|
||||
m_fields->emplace_back(m_accountEntry);
|
||||
m_fields->back().setName(value.toString().toStdString());
|
||||
endInsertRows();
|
||||
roles << role;
|
||||
break;
|
||||
case 1:
|
||||
beginInsertRows(index.parent(), rowCount(), rowCount());
|
||||
m_fields->emplace_back(m_accountEntry);
|
||||
m_fields->back().setValue(value.toString().toStdString());
|
||||
endInsertRows();
|
||||
roles << role;
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
// return false if nothing could be changed
|
||||
if(roles.isEmpty()) {
|
||||
return false;
|
||||
} else {
|
||||
// some roles affect other roles
|
||||
switch(role) {
|
||||
case Qt::EditRole:
|
||||
roles << Qt::DisplayRole;
|
||||
break;
|
||||
case FieldTypeRole:
|
||||
roles << Qt::DisplayRole << Qt::EditRole;
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
// emit data changed signal on sucess
|
||||
emit dataChanged(index, index, roles);
|
||||
return true;
|
||||
}
|
||||
|
||||
Qt::ItemFlags FieldModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
return QAbstractTableModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
|
||||
}
|
||||
|
||||
QVariant FieldModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
switch(orientation) {
|
||||
case Qt::Horizontal:
|
||||
switch(role) {
|
||||
case Qt::DisplayRole:
|
||||
switch(section) {
|
||||
case 0:
|
||||
return tr("Name");
|
||||
case 1:
|
||||
return tr("Value");
|
||||
default:
|
||||
;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
int FieldModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
return (!parent.isValid() && m_fields) ? m_fields->size() + 1 : 0;
|
||||
}
|
||||
|
||||
int FieldModel::columnCount(const QModelIndex &parent) const
|
||||
{
|
||||
return !parent.isValid() ? 2 : 0;
|
||||
}
|
||||
|
||||
bool FieldModel::insertRows(int row, int count, const QModelIndex &parent)
|
||||
{
|
||||
#ifdef MODEL_UNDO_SUPPORT
|
||||
if(undoStack()) {
|
||||
return push(new FieldModelInsertRowsCommand(this, row, count));
|
||||
}
|
||||
#endif
|
||||
if(!parent.isValid() && row >= 0 && count > 0 && static_cast<size_t>(row) <= m_fields->size()) {
|
||||
beginInsertRows(parent, row, row + count - 1);
|
||||
m_fields->insert(m_fields->begin() + row, count, Field(m_accountEntry));
|
||||
endInsertRows();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FieldModel::removeRows(int row, int count, const QModelIndex &parent)
|
||||
{
|
||||
#ifdef MODEL_UNDO_SUPPORT
|
||||
if(undoStack()) {
|
||||
return push(new FieldModelRemoveRowsCommand(this, row, count));
|
||||
}
|
||||
#endif
|
||||
if(!parent.isValid() && row >= 0 && count > 0 && static_cast<size_t>(row + count) <= m_fields->size()) {
|
||||
beginRemoveRows(parent, row, row + count - 1);
|
||||
m_fields->erase(m_fields->begin() + row, m_fields->begin() + row + count);
|
||||
endRemoveRows();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FieldModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
|
||||
{
|
||||
if(!QAbstractTableModel::dropMimeData(data, action, row, column, parent)) {
|
||||
if(data->hasText()) {
|
||||
return setData(parent, data->text(), Qt::EditRole);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList FieldModel::mimeTypes() const
|
||||
{
|
||||
return QAbstractTableModel::mimeTypes() << QStringLiteral("text/plain");
|
||||
}
|
||||
|
||||
QMimeData *FieldModel::mimeData(const QModelIndexList &indexes) const
|
||||
{
|
||||
QMimeData *data = QAbstractTableModel::mimeData(indexes);
|
||||
if(!indexes.isEmpty()) {
|
||||
QStringList result;
|
||||
foreach(const QModelIndex &index, indexes) {
|
||||
result << index.data(Qt::EditRole).toString();
|
||||
}
|
||||
data->setText(result.join(QChar('\n')));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the field for the specified row.
|
||||
*
|
||||
* Might be nullptr if no account entry is assigned or if the row is out of range.
|
||||
*/
|
||||
const Field *FieldModel::field(size_t row) const
|
||||
{
|
||||
if(m_fields && row < m_fields->size()) {
|
||||
return &m_fields->at(row);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
#ifndef FIELDMODEL_H
|
||||
#define FIELDMODEL_H
|
||||
|
||||
#include <passwordfile/io/entry.h>
|
||||
#ifdef MODEL_UNDO_SUPPORT
|
||||
#include "gui/stacksupport.h"
|
||||
#endif
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Io {
|
||||
class Field;
|
||||
}
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
/*!
|
||||
* \brief The FieldModelRoles enum defines custom roles for the FieldModel class.
|
||||
*/
|
||||
enum FieldModelRoles
|
||||
{
|
||||
FieldTypeRole = Qt::UserRole + 1 /**< the field type */
|
||||
};
|
||||
|
||||
class FieldModel : public QAbstractTableModel
|
||||
#ifdef MODEL_UNDO_SUPPORT
|
||||
, public StackSupport
|
||||
#endif
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FieldModel(QObject *parent = nullptr);
|
||||
#ifdef MODEL_UNDO_SUPPORT
|
||||
explicit FieldModel(QUndoStack *undoStack, QObject *parent = nullptr);
|
||||
#endif
|
||||
|
||||
Io::AccountEntry *accountEntry();
|
||||
const Io::AccountEntry *accountEntry() const;
|
||||
void setAccountEntry(Io::AccountEntry *entry);
|
||||
std::vector<Io::Field> *fields();
|
||||
bool hidePasswords() const;
|
||||
QVariant data(const QModelIndex &index, int role) const;
|
||||
QMap<int, QVariant> itemData(const QModelIndex &index) const;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role);
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const;
|
||||
bool insertRows(int row, int count, const QModelIndex &parent);
|
||||
bool removeRows(int row, int count, const QModelIndex &parent);
|
||||
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
|
||||
QStringList mimeTypes() const;
|
||||
QMimeData *mimeData(const QModelIndexList &indexes) const;
|
||||
const Io::Field *field(std::size_t row) const;
|
||||
|
||||
public slots:
|
||||
void setHidePasswords(bool hidePasswords);
|
||||
void reset();
|
||||
|
||||
private:
|
||||
Io::AccountEntry *m_accountEntry;
|
||||
std::vector<Io::Field> *m_fields;
|
||||
bool m_hidePasswords;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Returns the account entry. Might be nullptr if no account entry has been set.
|
||||
*
|
||||
* The ownership remains unaltered.
|
||||
*/
|
||||
inline Io::AccountEntry *FieldModel::accountEntry()
|
||||
{
|
||||
return m_accountEntry;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the account entry. Might be nullptr if no account entry has been set.
|
||||
*
|
||||
* The ownership remains unaltered.
|
||||
*/
|
||||
inline const Io::AccountEntry *FieldModel::accountEntry() const
|
||||
{
|
||||
return m_accountEntry;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the fields of the account entry. Might be nullptr if no account entry is assigned.
|
||||
*
|
||||
* The ownership remains unaltered.
|
||||
*/
|
||||
inline std::vector<Io::Field> *FieldModel::fields()
|
||||
{
|
||||
return m_fields;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Resets the model. The account entry will be unset.
|
||||
*/
|
||||
inline void FieldModel::reset()
|
||||
{
|
||||
setAccountEntry(nullptr);
|
||||
}
|
||||
|
||||
inline bool FieldModel::hidePasswords() const
|
||||
{
|
||||
return m_hidePasswords;
|
||||
}
|
||||
|
||||
inline void FieldModel::setHidePasswords(bool hidePasswords)
|
||||
{
|
||||
m_hidePasswords = hidePasswords;
|
||||
if(m_fields) {
|
||||
emit dataChanged(index(0, 1), index(m_fields->size() - 1, 1), QVector<int>() << Qt::DisplayRole);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // FIELDMODEL_H
|
|
@ -0,0 +1,116 @@
|
|||
projectname = passwordmanager
|
||||
|
||||
# include ../../common.pri when building as part of a subdirs project; otherwise include general.pri
|
||||
!include(../../common.pri) {
|
||||
!include(./general.pri) {
|
||||
error("Couldn't find the common.pri or the general.pri file!")
|
||||
}
|
||||
}
|
||||
|
||||
TEMPLATE = app
|
||||
|
||||
no-gui {
|
||||
CONFIG -= qt # Qt is only required for GUI
|
||||
} else {
|
||||
QT += core gui
|
||||
android {
|
||||
OTHER_FILES += android/AndroidManifest.xml
|
||||
include(../../deployment.pri)
|
||||
}
|
||||
}
|
||||
|
||||
guiqtquick {
|
||||
QML_IMPORT_PATH += ./quick ./qml ./qml/pages ./qml/touch
|
||||
}
|
||||
|
||||
# files
|
||||
SOURCES += main.cpp\
|
||||
model/entrymodel.cpp \
|
||||
model/fieldmodel.cpp \
|
||||
model/entryfiltermodel.cpp \
|
||||
util/testroutines.cpp \
|
||||
cli/cli.cpp
|
||||
|
||||
|
||||
guiqtwidgets {
|
||||
SOURCES += gui/mainwindow.cpp \
|
||||
gui/passwordgeneratordialog.cpp \
|
||||
gui/undocommands.cpp \
|
||||
gui/stacksupport.cpp \
|
||||
gui/initiatequi.cpp
|
||||
|
||||
FORMS += gui/mainwindow.ui \
|
||||
gui/passwordgeneratordialog.ui
|
||||
}
|
||||
|
||||
guiqtquick {
|
||||
SOURCES += quickgui/applicationinfo.cpp \
|
||||
quickgui/initiatequick.cpp
|
||||
}
|
||||
|
||||
HEADERS += model/entrymodel.h \
|
||||
model/fieldmodel.h \
|
||||
model/entryfiltermodel.h \
|
||||
util/testroutines.h \
|
||||
cli/cli.h
|
||||
|
||||
guiqtwidgets {
|
||||
HEADERS += gui/mainwindow.h \
|
||||
gui/passwordgeneratordialog.h \
|
||||
gui/undocommands.h \
|
||||
gui/stacksupport.h \
|
||||
gui/initiate.h
|
||||
}
|
||||
|
||||
guiqtquick {
|
||||
HEADERS += quickgui/applicationinfo.h \
|
||||
quickgui/applicationpaths.h
|
||||
quickgui/initiate.h
|
||||
}
|
||||
|
||||
# resources and translations
|
||||
!no-gui {
|
||||
RESOURCES += resources/icons.qrc
|
||||
contains(DEFINES, GUI_QTQUICK) {
|
||||
RESOURCES += resources/qml.qrc
|
||||
}
|
||||
|
||||
TRANSLATIONS = translations/passwordmanager_en_US.ts \
|
||||
translations/passwordmanager_de_DE.ts
|
||||
}
|
||||
updateqm.commands = lrelease $${projectname}.pro
|
||||
updateqm.target = updateqm
|
||||
QMAKE_EXTRA_TARGETS += updateqm
|
||||
|
||||
OTHER_FILES += \
|
||||
pkgbuild/default/PKGBUILD \
|
||||
pkgbuild/mingw-w64/PKGBUILD
|
||||
|
||||
# libs and includepath
|
||||
CONFIG(debug, debug|release) {
|
||||
LIBS += -L../../ -lc++utilitiesd -lpasswordfiled
|
||||
!no-gui {
|
||||
LIBS += -lqtutilitiesd
|
||||
}
|
||||
} else {
|
||||
LIBS += -L../../ -lc++utilities -lpasswordfile
|
||||
!no-gui {
|
||||
LIBS += -lqtutilities
|
||||
}
|
||||
}
|
||||
|
||||
# requried when building in subdirs project
|
||||
INCLUDEPATH += ../
|
||||
|
||||
# installs
|
||||
target.path = $$(INSTALL_ROOT)/bin
|
||||
INSTALLS += target
|
||||
icon.path = $$(INSTALL_ROOT)/share/icons/hicolor/scalable/apps/
|
||||
icon.files = ./resources/icons/hicolor/scalable/apps/$${projectname}.svg
|
||||
INSTALLS += icon
|
||||
menu.path = $$(INSTALL_ROOT)/share/applications/
|
||||
menu.files = ./resources/desktop/applications/$${projectname}.desktop
|
||||
INSTALLS += menu
|
||||
translations.path = $$(INSTALL_ROOT)/share/$${projectname}/translations/
|
||||
translations.files = ./translations/*.qm
|
||||
INSTALLS += translations
|
|
@ -0,0 +1,23 @@
|
|||
pkgname=passwordmanager
|
||||
pkgver=2.0.5
|
||||
pkgrel=2
|
||||
arch=('i686' 'x86_64')
|
||||
pkgdesc="A simple password store using AES-256-CBC encryption via OpenSSL."
|
||||
license=('GPL')
|
||||
depends=('qt5-base' 'qtutilities' 'passwordfile' 'openssl' 'libxkbcommon-x11')
|
||||
makedepends=('qt5-tools')
|
||||
install=${pkgname}.install
|
||||
url="http://martchus.netai.net/"
|
||||
source=("$pkgname.tar.gz")
|
||||
md5sums=('d359927292464fcf41c8a11315ff79e2')
|
||||
# head end
|
||||
build() {
|
||||
cd $srcdir/$pkgname
|
||||
INSTALL_ROOT=$pkgdir/usr/ qmake-qt5 "$pkgname.pro" -r -spec linux-g++
|
||||
make && make updateqm
|
||||
}
|
||||
|
||||
package() {
|
||||
cd $srcdir/$pkgname
|
||||
make install
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
post_install() {
|
||||
gtk-update-icon-cache -q -t -f /usr/share/icons/hicolor
|
||||
}
|
||||
|
||||
post_upgrade() {
|
||||
post_install
|
||||
}
|
||||
|
||||
post_remove() {
|
||||
post_install
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
_name=passwordmanager
|
||||
pkgname=mingw-w64-$_name
|
||||
pkgver=2.0.5
|
||||
pkgrel=2
|
||||
arch=('any')
|
||||
pkgdesc="A simple password store using AES-256-CBC encryption via OpenSSL (mingw-w64)."
|
||||
license=('GPL')
|
||||
depends=('mingw-w64-crt' 'mingw-w64-qt5-base' 'mingw-w64-qtutilities' 'mingw-w64-passwordfile' 'mingw-w64-openssl')
|
||||
makedepends=('mingw-w64-gcc')
|
||||
url="http://martchus.netai.net/"
|
||||
source=("${_name}.tar.gz")
|
||||
md5sums=('4e94aff9225d8873f752995c1bcc5f15')
|
||||
_architectures="i686-w64-mingw32 x86_64-w64-mingw32"
|
||||
# head end
|
||||
build() {
|
||||
cd $srcdir/$_name
|
||||
# build utilities for each architecture
|
||||
for _arch in ${_architectures}; do
|
||||
mkdir -p build-${_arch} && pushd build-${_arch}
|
||||
INSTALL_ROOT=$pkgdir/usr/ ${_arch}-qmake-qt5 -r ../${_name}.pro
|
||||
make
|
||||
popd
|
||||
done
|
||||
}
|
||||
|
||||
package() {
|
||||
cd $srcdir/$_name
|
||||
for _arch in ${_architectures}; do
|
||||
# bin stuff
|
||||
pushd build-${_arch}
|
||||
strip --strip-unneeded ./release/${_name}.exe
|
||||
install -m755 -D ./release/${_name}.exe $pkgdir/usr/${_arch}/bin/${_name}.exe
|
||||
popd
|
||||
done
|
||||
}
|
After Width: | Height: | Size: 376 B |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
|
@ -0,0 +1,175 @@
|
|||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.2
|
||||
import QtQuick.Controls.Styles 1.2
|
||||
import martchus.passwordmanager 2.0
|
||||
|
||||
import "./pages" as Pages
|
||||
import "./touch" as Touch
|
||||
|
||||
ApplicationWindow {
|
||||
id: root
|
||||
width: 700
|
||||
height: 800
|
||||
title: qsTr("Password Manager")
|
||||
visible: true
|
||||
|
||||
property string statusBarMessage
|
||||
|
||||
property Component startPage: Pages.StartPage {
|
||||
onUpdateStatusBar: statusBarMessage = message
|
||||
onNextPage: if (!isLocked) {
|
||||
pageView.push(accountsPage)
|
||||
clearSearchBox()
|
||||
}
|
||||
}
|
||||
property Component accountsPage: Pages.AccountsPage {
|
||||
onUpdateStatusBar: statusBarMessage = message
|
||||
onNextPage: if (!isLocked) {
|
||||
pageView.push(fieldsPage)
|
||||
}
|
||||
onPreviousPage: {
|
||||
pageView.pop()
|
||||
}
|
||||
}
|
||||
property Component fieldsPage: Pages.FieldsPage {
|
||||
onUpdateStatusBar: statusBarMessage = message
|
||||
onNextPage: if (!isLocked) {
|
||||
pageView.pop()
|
||||
}
|
||||
onPreviousPage: {
|
||||
pageView.pop()
|
||||
}
|
||||
}
|
||||
|
||||
StackView {
|
||||
id: pageView
|
||||
anchors.fill: parent
|
||||
focus: true
|
||||
Keys.onReleased: {
|
||||
if (event.key === Qt.Key_Back ||
|
||||
(event.key === Qt.Key_Left && (event.modifiers & Qt.AltModifier))) {
|
||||
if (pageView.depth > 1) {
|
||||
event.accepted = true
|
||||
if (!currentItem.isLocked)
|
||||
currentItem.previousPage()
|
||||
} else {
|
||||
if (!currentItem.hasNoSearchText) {
|
||||
event.accepted = true
|
||||
currentItem.clearSearchBox()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
initialItem: startPage
|
||||
delegate: StackViewDelegate {
|
||||
pushTransition: StackViewTransition {
|
||||
function transitionFinished(properties)
|
||||
{
|
||||
properties.exitItem.opacity = 1
|
||||
}
|
||||
PropertyAnimation {
|
||||
target: enterItem
|
||||
property: "x"
|
||||
from: target.width
|
||||
to: 0
|
||||
duration: 500
|
||||
easing.type: Easing.OutSine
|
||||
}
|
||||
PropertyAnimation {
|
||||
target: exitItem
|
||||
property: "x"
|
||||
from: 0
|
||||
to: -target.width
|
||||
duration: 500
|
||||
easing.type: Easing.OutSine
|
||||
}
|
||||
}
|
||||
popTransition: StackViewTransition {
|
||||
function transitionFinished(properties)
|
||||
{
|
||||
properties.exitItem.opacity = 1
|
||||
}
|
||||
PropertyAnimation {
|
||||
target: enterItem
|
||||
property: "x"
|
||||
from: -target.width
|
||||
to: 0
|
||||
duration: 500
|
||||
easing.type: Easing.OutSine
|
||||
}
|
||||
PropertyAnimation {
|
||||
target: exitItem
|
||||
property: "x"
|
||||
from: 0
|
||||
to: target.width
|
||||
duration: 500
|
||||
easing.type: Easing.OutSine
|
||||
}
|
||||
}
|
||||
property Component replaceTransition: pushTransition
|
||||
}
|
||||
}
|
||||
statusBar: StatusBar {
|
||||
id: statusbar
|
||||
width: parent.width
|
||||
opacity: label.text !== "" ? 1 : 0
|
||||
property real statusBarHeight: 65 * ApplicationInfo.ratio
|
||||
height: label.text !== "" ? statusBarHeight : 0
|
||||
Behavior on height { NumberAnimation {easing.type: Easing.OutSine}}
|
||||
Behavior on opacity { NumberAnimation {}}
|
||||
style: StatusBarStyle {
|
||||
padding { left: 0; right: 0 ; top: 0 ; bottom: 0}
|
||||
property Component background: Rectangle {
|
||||
implicitHeight: 65 * ApplicationInfo.ratio
|
||||
implicitWidth: root.width
|
||||
color: ApplicationInfo.colors.smokeGray
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: Qt.darker(parent.color, 1.5)
|
||||
}
|
||||
Rectangle {
|
||||
y: 1
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: "white"
|
||||
}
|
||||
}
|
||||
}
|
||||
Touch.TouchLabel {
|
||||
id: label
|
||||
y: 32 * ApplicationInfo.ratio - height/2
|
||||
width: parent.width // The text will only wrap if an explicit width has been set
|
||||
text: statusBarMessage
|
||||
textFormat: Text.RichText
|
||||
onLinkActivated: Qt.openUrlExternally(link)
|
||||
wrapMode: Text.Wrap
|
||||
pixelSize: 18
|
||||
letterSpacing: -0.15
|
||||
color: ApplicationInfo.colors.mediumGray
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
function decreaseFontSizeOnNarrowScreen() {
|
||||
if (label.implicitHeight > statusbar.statusBarHeight)
|
||||
pixelSize = Math.floor(pixelSize * statusbar.statusBarHeight/label.implicitHeight)
|
||||
}
|
||||
onTextChanged: {
|
||||
if (text === "")
|
||||
pixelSize = 18
|
||||
else
|
||||
decreaseFontSizeOnNarrowScreen()
|
||||
}
|
||||
onWidthChanged: decreaseFontSizeOnNarrowScreen()
|
||||
}
|
||||
}
|
||||
|
||||
menuBar: MenuBar {
|
||||
Menu {
|
||||
title: qsTr("Application")
|
||||
MenuItem {
|
||||
text: qsTr("Exit")
|
||||
onTriggered: Qt.quit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.2
|
||||
import QtQuick.Controls.Styles 1.2
|
||||
import martchus.passwordmanager 2.0
|
||||
|
||||
BasicPage {
|
||||
id: accountsPage
|
||||
title1: qsTr("Accounts")
|
||||
|
||||
pageComponent: Item {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,182 @@
|
|||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.2
|
||||
import QtQuick.Controls.Styles 1.2
|
||||
import QtQuick.Layouts 1.1
|
||||
import martchus.passwordmanager 2.0
|
||||
|
||||
import "../touch" as Touch
|
||||
|
||||
Item {
|
||||
id: page
|
||||
signal updateStatusBar(string message)
|
||||
signal nextPage
|
||||
signal previousPage
|
||||
signal clearSearchBox
|
||||
|
||||
property Component pageComponent
|
||||
property bool isLocked: Stack.status !== Stack.Active
|
||||
property string title1
|
||||
property string title2
|
||||
property string title3
|
||||
|
||||
property alias searchText: searchField.text
|
||||
property alias hasNoSearchText: searchField.isEmpty
|
||||
signal searchBoxReturn
|
||||
property string statusBarMessageDefault
|
||||
|
||||
property alias topRect: topRect
|
||||
property color topRectColor: ApplicationInfo.colors.yetAnotherBlue
|
||||
|
||||
property bool searchFieldVisisble: false
|
||||
|
||||
Binding {
|
||||
target: ApplicationInfo
|
||||
property: "isPortraitMode"
|
||||
value: page.height > page.width
|
||||
when: !ApplicationInfo.isMobile
|
||||
}
|
||||
|
||||
Binding {
|
||||
target: ApplicationInfo
|
||||
property: "applicationWidth"
|
||||
value: page.width
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: topRect
|
||||
z: 2 // so flickable doesn't draw on top
|
||||
anchors.top: parent.top
|
||||
height: 80 * ApplicationInfo.ratio
|
||||
width: parent.width
|
||||
color: page.Stack.index !== 0 && mouseBack.pressed ?
|
||||
Qt.lighter(topRectColor, 1.2) : topRectColor
|
||||
Rectangle {
|
||||
color: Qt.lighter(parent.color, 1.2)
|
||||
height: 1
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 1
|
||||
width: parent.width
|
||||
}
|
||||
Rectangle {
|
||||
z: 2 // so flickable doesn't draw on top
|
||||
height: 1
|
||||
width: parent.width
|
||||
color: Qt.darker(ApplicationInfo.colors.blue, 1.6)
|
||||
anchors.bottom: parent.bottom
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: titleRow
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: 0
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
Separator {
|
||||
Layout.fillWidth: false
|
||||
Layout.preferredWidth: ApplicationInfo.hMargin
|
||||
}
|
||||
Image {
|
||||
source: ApplicationInfo.imagePath("backarrow.png")
|
||||
Layout.preferredWidth: 22 * ApplicationInfo.ratio
|
||||
Layout.preferredHeight: 35 * ApplicationInfo.ratio
|
||||
visible: page.Stack.index > 0
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: qsTr("Back")
|
||||
function accessiblePressAction () { backPressed() }
|
||||
}
|
||||
Rectangle {
|
||||
opacity: 0
|
||||
Layout.preferredWidth: 20 * ApplicationInfo.ratio
|
||||
Layout.fillHeight: true
|
||||
visible: page.Stack.index > 0
|
||||
}
|
||||
Touch.TouchLabel {
|
||||
id: t1
|
||||
text: title1 + " "
|
||||
color: ApplicationInfo.colors.white
|
||||
pixelSize: 30
|
||||
font.weight: Font.Bold
|
||||
Layout.maximumWidth: ApplicationInfo.applicationWidth - t3.implicitWidth - 2 * ApplicationInfo.hMargin - 5 * ApplicationInfo.ratio - 42 * ApplicationInfo.ratio
|
||||
Layout.alignment: Qt.AlignBaseline
|
||||
}
|
||||
Touch.TouchLabel {
|
||||
text: "- " + title2
|
||||
color: ApplicationInfo.colors.white
|
||||
visible: title2 !== ""
|
||||
pixelSize: 22
|
||||
letterSpacing: -0.15
|
||||
Layout.alignment: Qt.AlignBaseline
|
||||
Layout.maximumWidth: freeSpace > implicitWidth ? freeSpace : 0
|
||||
property real freeSpace: ApplicationInfo.applicationWidth - t1.width - t3.implicitWidth - 2 * ApplicationInfo.hMargin - 5 * ApplicationInfo.ratio - 42 * ApplicationInfo.ratio
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
height: 0
|
||||
}
|
||||
Touch.TouchLabel {
|
||||
id: t3
|
||||
text: title3
|
||||
color: ApplicationInfo.colors.white
|
||||
visible: title3 !== ""
|
||||
pixelSize: 22
|
||||
letterSpacing: -0.15
|
||||
Layout.alignment: Qt.AlignBaseline
|
||||
}
|
||||
Separator {
|
||||
Layout.fillWidth: false
|
||||
Layout.preferredWidth: ApplicationInfo.hMargin
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 5
|
||||
anchors.top: parent.bottom
|
||||
gradient: Gradient {
|
||||
GradientStop {position: 0 ; color: "#40000000"}
|
||||
GradientStop {position: 1 ; color: "#00000000"}
|
||||
}
|
||||
}
|
||||
MouseArea {
|
||||
id: mouseBack
|
||||
anchors.fill: parent
|
||||
onClicked: backPressed()
|
||||
}
|
||||
}
|
||||
|
||||
function backPressed() {
|
||||
if (!isLocked) page.previousPage()
|
||||
}
|
||||
|
||||
Touch.TouchTextField {
|
||||
id: searchField
|
||||
z: 2
|
||||
visible: searchFieldVisisble
|
||||
anchors.right: topRect.right
|
||||
anchors.top: topRect.top
|
||||
anchors.bottom: topRect.bottom
|
||||
width: ApplicationInfo.isPortraitMode ?
|
||||
parent.width - t1.implicitWidth - ApplicationInfo.ratio * 50 :
|
||||
parent.width/2.5
|
||||
anchors.leftMargin: topRect.width/2
|
||||
anchors.rightMargin: 20 * ApplicationInfo.ratio
|
||||
anchors.margins: 12 * ApplicationInfo.ratio
|
||||
|
||||
placeholderText: qsTr("filter")
|
||||
Layout.fillWidth: true
|
||||
Keys.onReturnPressed: page.searchBoxReturn()
|
||||
Keys.onEnterPressed: page.searchBoxReturn()
|
||||
onClearButtonClicked: page.clearSearchBox()
|
||||
}
|
||||
|
||||
Loader {
|
||||
sourceComponent: pageComponent
|
||||
anchors.top: topRect.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
width: parent.width
|
||||
Rectangle {
|
||||
z: -1
|
||||
anchors.fill: parent
|
||||
color: ApplicationInfo.colors.white
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.2
|
||||
import QtQuick.Controls.Styles 1.2
|
||||
import martchus.passwordmanager 2.0
|
||||
|
||||
BasicPage {
|
||||
id : fieldsPage
|
||||
title1: ApplicationInfo.currentAccountName
|
||||
title3: qsTr("Fields")
|
||||
|
||||
pageComponent: Item {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import QtQuick 2.1
|
||||
import QtQuick.Layouts 1.0
|
||||
import martchus.passwordmanager 2.0
|
||||
|
||||
Item {
|
||||
implicitHeight: ApplicationInfo.hMargin
|
||||
implicitWidth: ApplicationInfo.hMargin
|
||||
Layout.minimumHeight: 0
|
||||
Layout.minimumWidth: 0
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
import QtQuick 2.2
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Controls 1.2
|
||||
import QtQuick.Controls.Styles 1.2
|
||||
import QtQuick.Dialogs 1.2
|
||||
import martchus.passwordmanager 2.0
|
||||
|
||||
import "../touch" as Touch
|
||||
|
||||
BasicPage {
|
||||
id: startPage
|
||||
title1: qsTr("Password Manager")
|
||||
|
||||
searchFieldVisisble: true
|
||||
|
||||
FileDialog {
|
||||
id: fileDialog
|
||||
title: qsTr("Select a password file")
|
||||
selectExisting: true
|
||||
onAccepted: {
|
||||
ApplicationInfo.currentFile = fileUrl
|
||||
nextPage()
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.top: topRect.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: 5
|
||||
|
||||
Touch.TouchButton {
|
||||
text: qsTr("Create a new password file")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Touch.TouchButton {
|
||||
text: qsTr("Open an existing password file")
|
||||
onClicked: {
|
||||
ApplicationInfo.currentFile = "./test"
|
||||
nextPage() // fileDialog.visible = true
|
||||
}
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Touch.TouchLabel {
|
||||
text: qsTr("Recently opened files")
|
||||
}
|
||||
|
||||
Touch.TouchLabel {
|
||||
text: qsTr("Test 1")
|
||||
//Layout.fillWidth: true
|
||||
color: ApplicationInfo.colors.lightGray
|
||||
}
|
||||
Touch.TouchLabel {
|
||||
text: qsTr("Test 2")
|
||||
//Layout.fillWidth: true
|
||||
color: ApplicationInfo.colors.lightGray
|
||||
}
|
||||
Touch.TouchLabel {
|
||||
text: qsTr("Test 3")
|
||||
//Layout.fillWidth: true
|
||||
color: ApplicationInfo.colors.lightGray
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
import QtQuick 2.1
|
||||
import QtQuick.Layouts 1.0
|
||||
import martchus.passwordmanager 2.0
|
||||
|
||||
Rectangle {
|
||||
id: rect
|
||||
height: ApplicationInfo.constants.rowDelegateHeight
|
||||
width: parent.width
|
||||
signal clicked
|
||||
signal deleteEntry
|
||||
|
||||
color: mouseNext.pressed ? ApplicationInfo.colors.smokeGray : ApplicationInfo.colors.white
|
||||
|
||||
GridLayout {
|
||||
id: _grid
|
||||
anchors.fill: parent
|
||||
flow: Qt.LeftToRight
|
||||
rowSpacing: 4 * ApplicationInfo.ratio
|
||||
columnSpacing: 0
|
||||
columns: 2
|
||||
Rectangle {
|
||||
Layout.preferredWidth: ApplicationInfo.hMargin
|
||||
Layout.fillHeight: true
|
||||
opacity: 0
|
||||
}
|
||||
Loader {
|
||||
// sourceComponent:
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Rectangle {
|
||||
id: separator
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.columnSpan: 2
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
z: 1
|
||||
height: 1
|
||||
anchors.bottom: parent.bottom
|
||||
width: parent.width
|
||||
color: ApplicationInfo.colors.paleGray
|
||||
}
|
||||
MouseArea {
|
||||
id: mouseNext
|
||||
anchors.left: parent.left
|
||||
width: parent.width - 80 * ApplicationInfo.ratio - ApplicationInfo.hMargin
|
||||
height: parent.height
|
||||
onClicked: rect.clicked()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Controls.Styles 1.1
|
||||
import QtQuick.Layouts 1.0
|
||||
import martchus.passwordmanager 2.0
|
||||
|
||||
Button {
|
||||
id: button
|
||||
property int pixelSize: 34
|
||||
property real letterSpacing: -0.25
|
||||
property string text: ""
|
||||
|
||||
style: ButtonStyle {
|
||||
label: Label {
|
||||
id: label
|
||||
font.family: "Open Sans"
|
||||
font.pixelSize: pixelSize * ApplicationInfo.ratio * 1.1 // increasing fonts
|
||||
font.letterSpacing: letterSpacing * ApplicationInfo.ratio
|
||||
color: ApplicationInfo.colors.doubleDarkGray
|
||||
verticalAlignment: Text.AlignBottom
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
elide: Text.ElideRight
|
||||
linkColor: ApplicationInfo.colors.blue
|
||||
renderType: ApplicationInfo.isMobile ? Text.QtRendering : Text.NativeRendering
|
||||
text: button.text
|
||||
|
||||
Text {
|
||||
id: text
|
||||
visible: false
|
||||
font.family: label.font.family
|
||||
font.pixelSize: label.font.pixelSize
|
||||
font.letterSpacing: label.font.letterSpacing
|
||||
wrapMode: label.wrapMode
|
||||
elide: label.elide
|
||||
text: label.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property int maximumWidth: (ApplicationInfo.constants.isMobile ? ApplicationInfo.applicationWidth : 1120) / 4
|
||||
Layout.minimumWidth: Math.min(Layout.maximumWidth, implicitWidth + 1)
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.0
|
||||
import QtQuick.Layouts 1.0
|
||||
import martchus.passwordmanager 2.0
|
||||
|
||||
Label {
|
||||
id: label
|
||||
property int pixelSize: 34
|
||||
property real letterSpacing: -0.25
|
||||
font.family: "Open Sans"
|
||||
font.pixelSize: pixelSize * ApplicationInfo.ratio * 1.1 // increasing fonts
|
||||
font.letterSpacing: letterSpacing * ApplicationInfo.ratio
|
||||
color: ApplicationInfo.colors.doubleDarkGray
|
||||
verticalAlignment: Text.AlignBottom
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
elide: Text.ElideRight
|
||||
linkColor: ApplicationInfo.colors.blue
|
||||
renderType: ApplicationInfo.isMobile ? Text.QtRendering : Text.NativeRendering
|
||||
function expectedTextWidth(value)
|
||||
{
|
||||
text.text = value
|
||||
return text.width
|
||||
}
|
||||
property int maximumWidth: (ApplicationInfo.constants.isMobile ? ApplicationInfo.applicationWidth : 1120) / 4
|
||||
Layout.minimumWidth: Math.min(Layout.maximumWidth, implicitWidth + 1)
|
||||
Text {
|
||||
id: text
|
||||
visible: false
|
||||
font.family: label.font.family
|
||||
font.pixelSize: label.font.pixelSize
|
||||
font.letterSpacing: label.font.letterSpacing
|
||||
wrapMode: label.wrapMode
|
||||
elide: label.elide
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.0
|
||||
import QtQuick.Controls.Styles 1.0
|
||||
import martchus.passwordmanager 2.0
|
||||
|
||||
ScrollView {
|
||||
frameVisible: false
|
||||
style: ScrollViewStyle {
|
||||
property int handleWidth: 20 * ApplicationInfo.ratio
|
||||
transientScrollBars: true
|
||||
padding{ top: 4 ; bottom: 4 ; right: 4}
|
||||
property bool hovered: false
|
||||
}
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: ApplicationInfo.colors.white
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.0
|
||||
import QtQuick.Controls.Styles 1.0
|
||||
import martchus.passwordmanager 2.0
|
||||
|
||||
Slider {
|
||||
id: slider
|
||||
property real sliderHandleHeight: 0.
|
||||
property real sliderHandleWidth: 0.
|
||||
implicitHeight: sliderHandleHeight + ApplicationInfo.ratio * 25
|
||||
style: SliderStyle {
|
||||
groove: Rectangle {
|
||||
Rectangle {
|
||||
id: beforeHandle
|
||||
width: styleData.handlePosition
|
||||
height: 20 * ApplicationInfo.ratio
|
||||
color: ApplicationInfo.colors.blue
|
||||
radius: 90
|
||||
z: -1
|
||||
}
|
||||
Rectangle {
|
||||
id: afterHandle
|
||||
anchors.left: beforeHandle.right
|
||||
anchors.right: parent.right
|
||||
height: 20 * ApplicationInfo.ratio
|
||||
color: ApplicationInfo.colors.darkGray
|
||||
radius: 90
|
||||
z: -1
|
||||
}
|
||||
}
|
||||
handle: Item {
|
||||
width: sliderHandleWidth
|
||||
height: sliderHandleHeight
|
||||
Image {
|
||||
anchors.centerIn: parent
|
||||
source: ApplicationInfo.imagePath(control.pressed ? "Pointer_pressed.png" : "Pointer.png")
|
||||
width: sliderHandleWidth + 16 * ApplicationInfo.ratio
|
||||
height: sliderHandleHeight + 16 * ApplicationInfo.ratio
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.0
|
||||
import QtQuick.Controls.Styles 1.0
|
||||
import martchus.passwordmanager 2.0
|
||||
|
||||
TextField {
|
||||
id: textfield
|
||||
signal clearButtonClicked
|
||||
implicitWidth: parent.width
|
||||
property bool isEmpty: true
|
||||
style: TextFieldStyle {
|
||||
renderType: ApplicationInfo.isMobile ? Text.QtRendering : Text.NativeRendering
|
||||
background: Rectangle {
|
||||
radius: 8
|
||||
border.width: 1
|
||||
border.color: Qt.darker(ApplicationInfo.colors.blue, 1.6)
|
||||
color: ApplicationInfo.colors.white
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0 ; color: "#ddd"}
|
||||
GradientStop { position: 0.05 ; color: "#fff"}
|
||||
}
|
||||
implicitHeight: 60 * ApplicationInfo.ratio
|
||||
opacity: 1
|
||||
}
|
||||
padding.left : (12 + 50) * ApplicationInfo.ratio
|
||||
padding.right: (12 + 50) * ApplicationInfo.ratio
|
||||
font.pixelSize: 28 * ApplicationInfo.ratio
|
||||
font.family: "Open Sans"
|
||||
font.letterSpacing: -0.25 * ApplicationInfo.ratio
|
||||
selectedTextColor : ApplicationInfo.colors.lightGray
|
||||
selectionColor : ApplicationInfo.colors.darkBlue
|
||||
textColor : ApplicationInfo.colors.mediumGray
|
||||
}
|
||||
|
||||
onClearButtonClicked: text = ""
|
||||
|
||||
Item {
|
||||
id: item
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
height: parent.height
|
||||
width: parent.height
|
||||
Image {
|
||||
opacity: 0.9
|
||||
anchors.centerIn: item
|
||||
height: iconSize
|
||||
width: iconSize
|
||||
source: ApplicationInfo.imagePath("magnifier.png")
|
||||
property int iconSize: 50 * ApplicationInfo.ratio
|
||||
}
|
||||
}
|
||||
|
||||
onTextChanged: isEmpty = (text === "")
|
||||
inputMethodHints: Qt.ImhNoPredictiveText
|
||||
MouseArea {
|
||||
z: 2
|
||||
opacity: !textfield.isEmpty ? 1 : 0
|
||||
Behavior on opacity {NumberAnimation{}}
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 4 * ApplicationInfo.ratio
|
||||
anchors.top: parent.top
|
||||
height: parent.height
|
||||
width: parent.height
|
||||
Image {
|
||||
anchors.centerIn: parent
|
||||
source: ApplicationInfo.imagePath("clear.png")
|
||||
property int iconSize: 40 * ApplicationInfo.ratio
|
||||
opacity: parent.pressed ? 1 : 0.9
|
||||
width: iconSize
|
||||
height: iconSize
|
||||
}
|
||||
onClicked: textfield.clearButtonClicked()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
#include "applicationinfo.h"
|
||||
|
||||
#include <qmath.h>
|
||||
|
||||
#include <QFile>
|
||||
#include <QRegExp>
|
||||
#include <QUrl>
|
||||
#include <QUrlQuery>
|
||||
#include <QGuiApplication>
|
||||
#include <QScreen>
|
||||
#include <QDebug>
|
||||
|
||||
using namespace Io;
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
ApplicationInfo::ApplicationInfo()
|
||||
{
|
||||
m_colors = new QQmlPropertyMap(this);
|
||||
m_colors->insert(QLatin1String("white"), QVariant("#ffffff"));
|
||||
m_colors->insert(QLatin1String("smokeGray"), QVariant("#eeeeee"));
|
||||
m_colors->insert(QLatin1String("paleGray"), QVariant("#d7d6d5"));
|
||||
m_colors->insert(QLatin1String("lightGray"), QVariant("#aeadac"));
|
||||
m_colors->insert(QLatin1String("darkGray"), QVariant("#35322f"));
|
||||
m_colors->insert(QLatin1String("mediumGray"), QVariant("#5d5b59"));
|
||||
m_colors->insert(QLatin1String("doubleDarkGray"), QVariant("#1e1b18"));
|
||||
m_colors->insert(QLatin1String("blue"), QVariant("#14aaff"));
|
||||
m_colors->insert(QLatin1String("yetAnotherBlue"), QVariant("#428bca"));
|
||||
m_colors->insert(QLatin1String("darkBlue"), QVariant("#14148c"));
|
||||
m_colors->insert(QLatin1String("darkYellow"), QVariant("#dfdc00"));
|
||||
m_colors->insert(QLatin1String("darkYellow"), QVariant("#eb881c"));
|
||||
m_colors->insert(QLatin1String("almostBlack"), QVariant("#222222"));
|
||||
|
||||
m_constants = new QQmlPropertyMap(this);
|
||||
m_constants->insert(QLatin1String("isMobile"), QVariant(isMobile()));
|
||||
|
||||
QRect rect = QGuiApplication::primaryScreen()->geometry();
|
||||
m_ratio = isMobile() ? qMin(qMax(rect.width(), rect.height()) / 1136., qMin(rect.width(), rect.height()) / 640.) : .5;
|
||||
m_sliderHandleWidth = sizeWithRatio(70);
|
||||
m_sliderHandleHeight = sizeWithRatio(87);
|
||||
m_sliderGapWidth = sizeWithRatio(100);
|
||||
m_isPortraitMode = isMobile() ? rect.height() > rect.width() : false;
|
||||
m_hMargin = m_isPortraitMode ? 20 * ratio() : 50 * ratio();
|
||||
m_applicationWidth = isMobile() ? rect.width() : 1120;
|
||||
|
||||
m_constants->insert(QLatin1String("rowDelegateHeight"), QVariant(sizeWithRatio(118)));
|
||||
|
||||
m_fieldModel = new FieldModel(this);
|
||||
|
||||
if(isMobile()) {
|
||||
connect(QGuiApplication::primaryScreen(), &QScreen::orientationChanged, this, &ApplicationInfo::notifyPortraitMode);
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationInfo::setApplicationWidth(const int newWidth)
|
||||
{
|
||||
if (newWidth != m_applicationWidth) {
|
||||
m_applicationWidth = newWidth;
|
||||
emit applicationWidthChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QString ApplicationInfo::imagePath(const QString image)
|
||||
{
|
||||
return QStringLiteral("qrc:/qml/images/%1").arg(image);
|
||||
}
|
||||
|
||||
void ApplicationInfo::notifyPortraitMode(Qt::ScreenOrientation orientation)
|
||||
{
|
||||
switch (orientation) {
|
||||
case Qt::LandscapeOrientation:
|
||||
case Qt::InvertedLandscapeOrientation:
|
||||
setIsPortraitMode(false);
|
||||
break;
|
||||
case Qt::PortraitOrientation:
|
||||
case Qt::InvertedPortraitOrientation:
|
||||
setIsPortraitMode(true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationInfo::setIsPortraitMode(const bool newMode)
|
||||
{
|
||||
if (m_isPortraitMode != newMode) {
|
||||
m_isPortraitMode = newMode;
|
||||
m_hMargin = m_isPortraitMode ? 20 * ratio() : 50 * ratio();
|
||||
emit portraitModeChanged();
|
||||
emit hMarginChanged();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
#ifndef APPLICATIONINFO_H
|
||||
#define APPLICATIONINFO_H
|
||||
|
||||
#include "model/fieldmodel.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlPropertyMap>
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
class ApplicationInfo : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int applicationWidth READ applicationWidth WRITE setApplicationWidth NOTIFY applicationWidthChanged)
|
||||
Q_PROPERTY(bool isMobile READ isMobile CONSTANT)
|
||||
Q_PROPERTY(QObject *colors READ colors CONSTANT)
|
||||
Q_PROPERTY(QObject *constants READ constants CONSTANT)
|
||||
Q_PROPERTY(bool isPortraitMode READ isPortraitMode WRITE setIsPortraitMode NOTIFY portraitModeChanged)
|
||||
Q_PROPERTY(const QString ¤fFile READ currentFile WRITE setCurrentFile NOTIFY currentFileChanged)
|
||||
Q_PROPERTY(FieldModel *fieldModel READ fieldModel)
|
||||
Q_PROPERTY(Io::AccountEntry *currentAccountEntry READ currentAccountEntry WRITE setCurrentAccountEntry)
|
||||
Q_PROPERTY(QString currentAccountName READ currentAccountName)
|
||||
Q_PROPERTY(qreal ratio READ ratio CONSTANT)
|
||||
Q_PROPERTY(qreal hMargin READ hMargin NOTIFY hMarginChanged)
|
||||
Q_PROPERTY(qreal sliderHandleWidth READ sliderHandleWidth CONSTANT)
|
||||
Q_PROPERTY(qreal sliderHandleHeight READ sliderHandleHeight CONSTANT)
|
||||
Q_PROPERTY(qreal sliderGapWidth READ sliderGapWidth CONSTANT)
|
||||
|
||||
public:
|
||||
ApplicationInfo();
|
||||
|
||||
static constexpr bool isMobile();
|
||||
QQmlPropertyMap *colors() const;
|
||||
QQmlPropertyMap *constants() const;
|
||||
|
||||
int applicationWidth() const;
|
||||
void setApplicationWidth(const int newWidth);
|
||||
|
||||
bool isPortraitMode() const;
|
||||
void setIsPortraitMode(const bool newMode);
|
||||
|
||||
const QString ¤tFile() const;
|
||||
void setCurrentFile(const QString ¤tFile);
|
||||
FieldModel *fieldModel();
|
||||
Io::AccountEntry *currentAccountEntry();
|
||||
QString currentAccountName() const;
|
||||
void setCurrentAccountEntry(Io::AccountEntry *entry);
|
||||
|
||||
qreal hMargin() const;
|
||||
qreal ratio() const;
|
||||
qreal sliderHandleHeight();
|
||||
qreal sliderGapWidth();
|
||||
qreal sliderHandleWidth();
|
||||
|
||||
Q_INVOKABLE QString imagePath(const QString image);
|
||||
|
||||
protected slots:
|
||||
void notifyPortraitMode(Qt::ScreenOrientation);
|
||||
|
||||
protected:
|
||||
qreal sizeWithRatio(const qreal height);
|
||||
|
||||
signals:
|
||||
void currentFileChanged();
|
||||
void applicationWidthChanged();
|
||||
void portraitModeChanged();
|
||||
void hMarginChanged();
|
||||
|
||||
private:
|
||||
int m_applicationWidth;
|
||||
QQmlPropertyMap *m_colors;
|
||||
QQmlPropertyMap *m_constants;
|
||||
bool m_isPortraitMode;
|
||||
QString m_currentFile;
|
||||
FieldModel *m_fieldModel;
|
||||
qreal m_ratio;
|
||||
qreal m_hMargin;
|
||||
qreal m_sliderHandleHeight, m_sliderHandleWidth, m_sliderGapWidth;
|
||||
};
|
||||
|
||||
constexpr bool ApplicationInfo::isMobile()
|
||||
{
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(Q_OS_BLACKBERRY)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline QQmlPropertyMap *ApplicationInfo::colors() const
|
||||
{
|
||||
return m_colors;
|
||||
}
|
||||
|
||||
inline QQmlPropertyMap *ApplicationInfo::constants() const
|
||||
{
|
||||
return m_constants;
|
||||
}
|
||||
|
||||
inline int ApplicationInfo::applicationWidth() const
|
||||
{
|
||||
return m_applicationWidth;
|
||||
}
|
||||
|
||||
inline bool ApplicationInfo::isPortraitMode() const
|
||||
{
|
||||
return m_isPortraitMode;
|
||||
}
|
||||
|
||||
inline const QString &ApplicationInfo::currentFile() const
|
||||
{
|
||||
return m_currentFile;
|
||||
}
|
||||
|
||||
inline void ApplicationInfo::setCurrentFile(const QString ¤tFile)
|
||||
{
|
||||
if(m_currentFile != currentFile) {
|
||||
m_currentFile = currentFile;
|
||||
emit currentFileChanged();
|
||||
}
|
||||
}
|
||||
|
||||
inline FieldModel *ApplicationInfo::fieldModel()
|
||||
{
|
||||
return m_fieldModel;
|
||||
}
|
||||
|
||||
inline Io::AccountEntry *ApplicationInfo::currentAccountEntry()
|
||||
{
|
||||
return m_fieldModel->accountEntry();
|
||||
}
|
||||
|
||||
inline QString ApplicationInfo::currentAccountName() const
|
||||
{
|
||||
if(m_fieldModel->accountEntry()) {
|
||||
return QString::fromStdString(m_fieldModel->accountEntry()->label());
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
inline void ApplicationInfo::setCurrentAccountEntry(Io::AccountEntry *entry)
|
||||
{
|
||||
m_fieldModel->setAccountEntry(entry);
|
||||
}
|
||||
|
||||
inline qreal ApplicationInfo::hMargin() const
|
||||
{
|
||||
return m_hMargin;
|
||||
}
|
||||
|
||||
inline qreal ApplicationInfo::ratio() const
|
||||
{
|
||||
return m_ratio;
|
||||
}
|
||||
|
||||
inline qreal ApplicationInfo::sliderHandleHeight()
|
||||
{
|
||||
return m_sliderHandleHeight;
|
||||
}
|
||||
|
||||
inline qreal ApplicationInfo::sliderGapWidth()
|
||||
{
|
||||
return m_sliderGapWidth;
|
||||
}
|
||||
|
||||
inline qreal ApplicationInfo::sliderHandleWidth()
|
||||
{
|
||||
return m_sliderHandleWidth;
|
||||
}
|
||||
|
||||
inline qreal ApplicationInfo::sizeWithRatio(const qreal height)
|
||||
{
|
||||
return ratio() * height;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // APPLICATIONINFO_H
|
|
@ -0,0 +1,42 @@
|
|||
#ifndef APPLICATIONPATHS_H
|
||||
#define APPLICATIONPATHS_H
|
||||
|
||||
#include <QDir>
|
||||
#include <QStandardPaths>
|
||||
#include <QStringList>
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
class ApplicationPaths
|
||||
{
|
||||
public:
|
||||
static QString settingsPath();
|
||||
static QString dowloadedFilesPath();
|
||||
protected:
|
||||
static QString path(QStandardPaths::StandardLocation location);
|
||||
};
|
||||
|
||||
inline QString ApplicationPaths::settingsPath()
|
||||
{
|
||||
return path(QStandardPaths::DataLocation);
|
||||
}
|
||||
|
||||
inline QString ApplicationPaths::dowloadedFilesPath()
|
||||
{
|
||||
return path(QStandardPaths::CacheLocation);
|
||||
}
|
||||
|
||||
inline QString ApplicationPaths::path(QStandardPaths::StandardLocation location)
|
||||
{
|
||||
QString path = QStandardPaths::standardLocations(location).value(0);
|
||||
QDir dir(path);
|
||||
if(!dir.exists())
|
||||
dir.mkpath(path);
|
||||
if(!path.isEmpty() && !path.endsWith("/"))
|
||||
path += "/";
|
||||
return path;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // APPLICATIONPATHS_H
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef QT_QUICK_GUI_INITIATE_H
|
||||
#define QT_QUICK_GUI_INITIATE_H
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QString;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
int runQuickGui(int argc, char *argv[]);
|
||||
|
||||
}
|
||||
|
||||
#endif // QT_QUICK_GUI_INITIATE_H
|
|
@ -0,0 +1,66 @@
|
|||
#include "initiate.h"
|
||||
|
||||
# include "model/entryfiltermodel.h"
|
||||
# include "model/entrymodel.h"
|
||||
# include "model/fieldmodel.h"
|
||||
|
||||
# include "quickgui/applicationinfo.h"
|
||||
|
||||
#include <qtutilities/resources/resources.h>
|
||||
|
||||
#if defined(GUI_QTWIDGETS)
|
||||
# include <QApplication>
|
||||
#else
|
||||
# include <QGuiApplication>
|
||||
#endif
|
||||
#include <QGuiApplication>
|
||||
#include <QTextCodec>
|
||||
#include <QtQml>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QDebug>
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
#if defined(GUI_QTQUICK)
|
||||
static QObject *applicationInfo(QQmlEngine *engine, QJSEngine *scriptEngine)
|
||||
{
|
||||
Q_UNUSED(engine)
|
||||
Q_UNUSED(scriptEngine)
|
||||
return new ApplicationInfo();
|
||||
}
|
||||
#endif
|
||||
|
||||
int runQuickGui(int argc, char *argv[])
|
||||
{
|
||||
// init application
|
||||
#if defined(GUI_QTWIDGETS)
|
||||
QApplication a(argc, argv);
|
||||
#else
|
||||
QGuiApplication a(argc, argv);
|
||||
#endif
|
||||
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
|
||||
QGuiApplication::setOrganizationName(QStringLiteral("Martchus"));
|
||||
QGuiApplication::setOrganizationDomain(QStringLiteral("http://martchus.netai.net/"));
|
||||
QGuiApplication::setApplicationName(QStringLiteral("Password Manager (mobile)"));
|
||||
QGuiApplication::setApplicationVersion(QStringLiteral("2.0.5"));
|
||||
// load translation files
|
||||
TranslationFiles::loadQtTranslationFile();
|
||||
TranslationFiles::loadApplicationTranslationFile(QStringLiteral("passwordmanager"));
|
||||
// load the other resources
|
||||
QtUtilitiesResources::init();
|
||||
Theme::setup();
|
||||
qDebug() << "test 2";
|
||||
// init quick GUI
|
||||
qmlRegisterSingletonType<QtGui::ApplicationInfo>("martchus.passwordmanager", 2, 0, "ApplicationInfo", applicationInfo);
|
||||
qmlRegisterType<QtGui::EntryFilterModel>("martchus.passwordmanager", 2, 0, "EntryFilterModel");
|
||||
qmlRegisterType<QtGui::EntryModel>("martchus.passwordmanager", 2, 0, "EntryModel");
|
||||
qmlRegisterType<QtGui::FieldModel>("martchus.passwordmanager", 2, 0, "FieldModel");
|
||||
QQmlApplicationEngine engine(QUrl("qrc:/qml/main.qml"));
|
||||
// start event loop
|
||||
int res = a.exec();
|
||||
// cleanup resources
|
||||
QtUtilitiesResources::cleanup();
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
[Desktop Entry]
|
||||
Name=Password manager
|
||||
GenericName=Password manager
|
||||
Comment=A simple password store using AES-256-CBC encryption via OpenSSL.
|
||||
Exec=passwordmanager
|
||||
Icon=passwordmanager
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Utility
|
|
@ -0,0 +1,5 @@
|
|||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>icons/hicolor/128x128/apps/passwordmanager.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
After Width: | Height: | Size: 3.8 KiB |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" ?><svg height="24" version="1.1" width="24" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><g transform="translate(0 -1028.4)"><path d="m12 1031.4c-2.7614 0-5 2.6-5 6v1c0 0.5 0.3731 1 0.8333 1 0.4603 0 0.8334-0.5 0.8334-1v-1c0-2.4 1.4923-4.3 3.3333-4.3s3.333 1.9 3.333 4.3v1c0 0.5 0.373 1 0.834 1 0.46 0 0.833-0.5 0.833-1v-1c0-3.4-2.239-6-5-6z" fill="#7f8c8d"/><path d="m6 1037.4c-1.1046 0-2 0.9-2 2v7c0 1.1 0.8954 2 2 2h12c1.105 0 2-0.9 2-2v-7c0-1.1-0.895-2-2-2h-12z" fill="#f1c40f"/><path d="m12 1029.9c-3.3137 0-6 2.7-6 6v1c0 0.6 0.4477 1 1 1s1-0.4 1-1v-1c0-2.3 1.7909-4.2 4-4.2 2.209 0 4 1.9 4 4.2v1c0 0.6 0.448 1 1 1s1-0.4 1-1v-1c0-3.3-2.686-6-6-6z" fill="#bdc3c7"/><path d="m6 1039.4c-1.1046 0-2 0.9-2 2v7c0 1.1 0.8954 2 2 2h12c1.105 0 2-0.9 2-2v-7c0-1.1-0.895-2-2-2h-12z" fill="#f39c12"/><path d="m6.327 1037.4c-0.3403 0-0.6569 0.1-0.9375 0.2 0.222 0.9 1.0056 1.5 1.9375 1.5 1.0193 0 1.845-0.8 1.9688-1.7h-2.9688z" fill="#7f8c8d"/><path d="m17.792 1037.3c0.34 0 0.657 0.1 0.937 0.3-0.222 0.8-1.005 1.5-1.937 1.5-1.02 0-1.845-0.8-1.969-1.8h2.969z" fill="#7f8c8d"/><path d="m12 1030.4c-3.3137 0-6 2.6-6 6v1c0 0.5 0.4477 1 1 1s1-0.5 1-1v-1c0-2.4 1.7909-4.3 4-4.3 2.209 0 4 1.9 4 4.3v1c0 0.5 0.448 1 1 1s1-0.5 1-1v-1c0-3.4-2.686-6-6-6z" fill="#95a5a6"/><g fill="#e67e22"><rect height="1" width="12" x="6" y="1041.4"/><rect height="1" width="12" x="6" y="1043.4"/><rect height="1" width="12" x="6" y="1045.4"/><rect height="1" width="12" x="6" y="1047.4"/></g></g></svg>
|
After Width: | Height: | Size: 1.6 KiB |
|
@ -0,0 +1,20 @@
|
|||
<RCC>
|
||||
<qresource prefix="/qml">
|
||||
<file alias="main.qml">../qml/main.qml</file>
|
||||
<file alias="pages/AccountsPage.qml">../qml/pages/AccountsPage.qml</file>
|
||||
<file alias="pages/BasicPage.qml">../qml/pages/BasicPage.qml</file>
|
||||
<file alias="pages/FieldsPage.qml">../qml/pages/FieldsPage.qml</file>
|
||||
<file alias="pages/StartPage.qml">../qml/pages/StartPage.qml</file>
|
||||
<file alias="pages/Separator.qml">../qml/pages/Separator.qml</file>
|
||||
<file alias="touch/TouchButton.qml">../qml/touch/TouchButton.qml</file>
|
||||
<file alias="touch/TouchLabel.qml">../qml/touch/TouchLabel.qml</file>
|
||||
<file alias="touch/TouchTextField.qml">../qml/touch/TouchTextField.qml</file>
|
||||
<file alias="touch/TouchSlider.qml">../qml/touch/TouchSlider.qml</file>
|
||||
<file alias="touch/TouchScrollView.qml">../qml/touch/TouchScrollView.qml</file>
|
||||
<file alias="touch/ListViewDelegate.qml">../qml/touch/ListViewDelegate.qml</file>
|
||||
<file alias="images/clear.png">../qml/images/clear.png</file>
|
||||
<file alias="images/magnifier.png">../qml/images/magnifier.png</file>
|
||||
<file alias="images/backarrow.png">../qml/images/backarrow.png</file>
|
||||
<file alias="images/close.png">../qml/images/close.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
|
@ -0,0 +1 @@
|
|||
IDI_ICON1 ICON DISCARDABLE "PasswordManager\resources\icon.ico"
|
|
@ -0,0 +1,620 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1" language="de_DE">
|
||||
<context>
|
||||
<name>QtGui::EntryModel</name>
|
||||
<message>
|
||||
<location filename="../model/entrymodel.cpp" line="329"/>
|
||||
<source>Name</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtGui::FieldModel</name>
|
||||
<message>
|
||||
<location filename="../model/fieldmodel.cpp" line="197"/>
|
||||
<source>Name</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../model/fieldmodel.cpp" line="199"/>
|
||||
<source>Value</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtGui::MainWindow</name>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="14"/>
|
||||
<source>Password Manager</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="98"/>
|
||||
<source>filter</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="188"/>
|
||||
<source>File</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="195"/>
|
||||
<source>Recent</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="221"/>
|
||||
<source>?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="227"/>
|
||||
<source>Edit</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="242"/>
|
||||
<source>Tools</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="263"/>
|
||||
<source>Open ...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="266"/>
|
||||
<source>Ctrl+O</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="276"/>
|
||||
<source>Quit</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="279"/>
|
||||
<source>Ctrl+Q</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="289"/>
|
||||
<source>About</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="299"/>
|
||||
<source>Save</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="302"/>
|
||||
<source>Ctrl+S</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="312"/>
|
||||
<source>New ...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="315"/>
|
||||
<source>Ctrl+N</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="325"/>
|
||||
<source>Close</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="328"/>
|
||||
<source>Ctrl+Shift+Q</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="338"/>
|
||||
<source>Change password ...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="348"/>
|
||||
<source>Add new account</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="351"/>
|
||||
<source>Ctrl+Shift+A</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="361"/>
|
||||
<source>Save as ...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="364"/>
|
||||
<source>Ctrl+Shift+S</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="374"/>
|
||||
<location filename="../gui/mainwindow.ui" line="377"/>
|
||||
<source>Remove selected entry</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="387"/>
|
||||
<location filename="../gui/mainwindow.cpp" line="871"/>
|
||||
<source>Insert field</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="397"/>
|
||||
<source>Remove selected field(s)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="402"/>
|
||||
<source>Password generator</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="412"/>
|
||||
<source>Clear list</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="423"/>
|
||||
<source>Always create backup</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="433"/>
|
||||
<source>Export ...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="443"/>
|
||||
<source>Show containing directory</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="456"/>
|
||||
<source>Undo</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="469"/>
|
||||
<source>Redo</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="479"/>
|
||||
<source>Add new category</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="484"/>
|
||||
<source>Show stack</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="195"/>
|
||||
<source>A simple password store using AES-256-CBC encryption via OpenSSL.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="216"/>
|
||||
<source>Select a password list</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="236"/>
|
||||
<source>The selected file can't be found anymore. Do you want to delete the obsolete entry from the list?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="238"/>
|
||||
<source>keep entry</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="239"/>
|
||||
<source>delete entry</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="270"/>
|
||||
<source>Undo stack</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="301"/>
|
||||
<source>The file you want to load seems to be very big. Do you really want to open it?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="310"/>
|
||||
<source>Enter the password to open the file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="315"/>
|
||||
<location filename="../gui/mainwindow.cpp" line="316"/>
|
||||
<source>A password is needed to open the file.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="335"/>
|
||||
<source>The file couldn't be decrypted.
|
||||
OpenSSL error queue: %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="339"/>
|
||||
<source>Unable to parse the file. %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="397"/>
|
||||
<source>The file <i>%1</i> couldn't be created.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="414"/>
|
||||
<source>A new password list has been created.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="418"/>
|
||||
<source>The password list "%1" has been load.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="481"/>
|
||||
<source>Select where you want to save the password list</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="483"/>
|
||||
<source>All files (*.*)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="485"/>
|
||||
<source>The file was not be saved.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="506"/>
|
||||
<source>There is no password list opened.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="519"/>
|
||||
<source>There's no account selected.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="536"/>
|
||||
<source>The password file has been modified.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="537"/>
|
||||
<source>Do you want to save the changes before closing?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="557"/>
|
||||
<source>The password list has been closed.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="580"/>
|
||||
<source>The backup file couldn't be created. %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="595"/>
|
||||
<source>Enter a password to save the file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="603"/>
|
||||
<source>The file hasn't been saved.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="612"/>
|
||||
<source>The password list couldn't be saved due to encryption failure.
|
||||
OpenSSL error queue: %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="624"/>
|
||||
<source>The password list has been saved.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="643"/>
|
||||
<source>The password list couldn't be exported. %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="646"/>
|
||||
<source>The password list has been exported.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="662"/>
|
||||
<source>The currently opened file hasn't been saved yet.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="676"/>
|
||||
<location filename="../gui/mainwindow.cpp" line="856"/>
|
||||
<source>Add account</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="684"/>
|
||||
<location filename="../gui/mainwindow.cpp" line="857"/>
|
||||
<source>Add category</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="702"/>
|
||||
<source>Enter the entry name</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="702"/>
|
||||
<source>new entry</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="711"/>
|
||||
<source>Unable to create new entry.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="714"/>
|
||||
<source>You didn't enter text.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="720"/>
|
||||
<source>No node element selected.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="735"/>
|
||||
<source>Unable to remove the entry.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="738"/>
|
||||
<source>No entry selected.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="776"/>
|
||||
<source>A field has to be selected since new fields are always inserted before the currently selected field.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="800"/>
|
||||
<source>No fields have been removed since there are currently no fields selected.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="821"/>
|
||||
<source>You didn't enter a password. <strong>No encryption</strong> will be used when saving the file next time.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="825"/>
|
||||
<source>The new password will be used next time you save the file.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="829"/>
|
||||
<source>You aborted. The old password will still be used when saving the file next time.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="858"/>
|
||||
<source>Remove entry</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="872"/>
|
||||
<source>Remove field</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="874"/>
|
||||
<source>Copy for 5 seconds</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="893"/>
|
||||
<source>The selected cell is empty.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="896"/>
|
||||
<source>Exactly one cell needs to be selected.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtGui::PasswordGeneratorDialog</name>
|
||||
<message>
|
||||
<location filename="../gui/passwordgeneratordialog.ui" line="32"/>
|
||||
<source>Password Generator</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/passwordgeneratordialog.ui" line="117"/>
|
||||
<source>Use small letters</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/passwordgeneratordialog.ui" line="127"/>
|
||||
<source>Use capital letters</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/passwordgeneratordialog.ui" line="137"/>
|
||||
<source>Use digits</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/passwordgeneratordialog.ui" line="174"/>
|
||||
<source>Length</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/passwordgeneratordialog.ui" line="181"/>
|
||||
<source>Other characters to be used</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/passwordgeneratordialog.ui" line="188"/>
|
||||
<source>!"§$%&/()=?;:_'*~’#,.-+<>|</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/passwordgeneratordialog.ui" line="229"/>
|
||||
<source><b>Password:</b></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/passwordgeneratordialog.ui" line="248"/>
|
||||
<source>Generate new password</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/passwordgeneratordialog.ui" line="264"/>
|
||||
<source>Copy password</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/passwordgeneratordialog.ui" line="311"/>
|
||||
<source>Close</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/passwordgeneratordialog.cpp" line="130"/>
|
||||
<source>Failed to generate password.
|
||||
OpenSSL error: %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/passwordgeneratordialog.cpp" line="133"/>
|
||||
<source>You have to select at least one checkbox.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/passwordgeneratordialog.cpp" line="136"/>
|
||||
<source>The length has to be at least one.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>undocommands</name>
|
||||
<message>
|
||||
<location filename="../gui/undocommands.cpp" line="76"/>
|
||||
<source>setting field name to »%1«</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/undocommands.cpp" line="78"/>
|
||||
<source>setting field name »%1« to »%2«</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/undocommands.cpp" line="84"/>
|
||||
<source>setting value of empty field to »%1«</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/undocommands.cpp" line="86"/>
|
||||
<source>setting value of »%1« field to »%2«</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="../gui/undocommands.cpp" line="117"/>
|
||||
<source>insertion of %1 row(s) before row %2</source>
|
||||
<translation type="unfinished">
|
||||
<numerusform></numerusform>
|
||||
<numerusform></numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="../gui/undocommands.cpp" line="145"/>
|
||||
<source>removal of row %1</source>
|
||||
<translation type="unfinished">
|
||||
<numerusform></numerusform>
|
||||
<numerusform></numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="../gui/undocommands.cpp" line="147"/>
|
||||
<source>removal of the rows %1 to %2</source>
|
||||
<translation type="unfinished">
|
||||
<numerusform></numerusform>
|
||||
<numerusform></numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/undocommands.cpp" line="188"/>
|
||||
<source>selection of account »%1«</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/undocommands.cpp" line="190"/>
|
||||
<source>account selection repealed</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/undocommands.cpp" line="254"/>
|
||||
<source>setting entry name to »%1«</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/undocommands.cpp" line="256"/>
|
||||
<source>setting entry name from »%1« to »%2«</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="../gui/undocommands.cpp" line="297"/>
|
||||
<source>insertion of %1 entry/entries</source>
|
||||
<translation type="unfinished">
|
||||
<numerusform></numerusform>
|
||||
<numerusform></numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="../gui/undocommands.cpp" line="332"/>
|
||||
<source>removal of %1 entry/entries</source>
|
||||
<translation type="unfinished">
|
||||
<numerusform></numerusform>
|
||||
<numerusform></numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="../gui/undocommands.cpp" line="372"/>
|
||||
<source>move of %1 entry/entries</source>
|
||||
<translation type="unfinished">
|
||||
<numerusform></numerusform>
|
||||
<numerusform></numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
|
@ -0,0 +1,620 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1" language="en_US">
|
||||
<context>
|
||||
<name>QtGui::EntryModel</name>
|
||||
<message>
|
||||
<location filename="../model/entrymodel.cpp" line="329"/>
|
||||
<source>Name</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtGui::FieldModel</name>
|
||||
<message>
|
||||
<location filename="../model/fieldmodel.cpp" line="197"/>
|
||||
<source>Name</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../model/fieldmodel.cpp" line="199"/>
|
||||
<source>Value</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtGui::MainWindow</name>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="14"/>
|
||||
<source>Password Manager</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="98"/>
|
||||
<source>filter</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="188"/>
|
||||
<source>File</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="195"/>
|
||||
<source>Recent</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="221"/>
|
||||
<source>?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="227"/>
|
||||
<source>Edit</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="242"/>
|
||||
<source>Tools</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="263"/>
|
||||
<source>Open ...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="266"/>
|
||||
<source>Ctrl+O</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="276"/>
|
||||
<source>Quit</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="279"/>
|
||||
<source>Ctrl+Q</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="289"/>
|
||||
<source>About</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="299"/>
|
||||
<source>Save</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="302"/>
|
||||
<source>Ctrl+S</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="312"/>
|
||||
<source>New ...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="315"/>
|
||||
<source>Ctrl+N</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="325"/>
|
||||
<source>Close</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="328"/>
|
||||
<source>Ctrl+Shift+Q</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="338"/>
|
||||
<source>Change password ...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="348"/>
|
||||
<source>Add new account</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="351"/>
|
||||
<source>Ctrl+Shift+A</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="361"/>
|
||||
<source>Save as ...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="364"/>
|
||||
<source>Ctrl+Shift+S</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="374"/>
|
||||
<location filename="../gui/mainwindow.ui" line="377"/>
|
||||
<source>Remove selected entry</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="387"/>
|
||||
<location filename="../gui/mainwindow.cpp" line="871"/>
|
||||
<source>Insert field</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="397"/>
|
||||
<source>Remove selected field(s)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="402"/>
|
||||
<source>Password generator</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="412"/>
|
||||
<source>Clear list</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="423"/>
|
||||
<source>Always create backup</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="433"/>
|
||||
<source>Export ...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="443"/>
|
||||
<source>Show containing directory</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="456"/>
|
||||
<source>Undo</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="469"/>
|
||||
<source>Redo</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="479"/>
|
||||
<source>Add new category</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.ui" line="484"/>
|
||||
<source>Show stack</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="195"/>
|
||||
<source>A simple password store using AES-256-CBC encryption via OpenSSL.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="216"/>
|
||||
<source>Select a password list</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="236"/>
|
||||
<source>The selected file can't be found anymore. Do you want to delete the obsolete entry from the list?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="238"/>
|
||||
<source>keep entry</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="239"/>
|
||||
<source>delete entry</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="270"/>
|
||||
<source>Undo stack</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="301"/>
|
||||
<source>The file you want to load seems to be very big. Do you really want to open it?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="310"/>
|
||||
<source>Enter the password to open the file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="315"/>
|
||||
<location filename="../gui/mainwindow.cpp" line="316"/>
|
||||
<source>A password is needed to open the file.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="335"/>
|
||||
<source>The file couldn't be decrypted.
|
||||
OpenSSL error queue: %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="339"/>
|
||||
<source>Unable to parse the file. %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="397"/>
|
||||
<source>The file <i>%1</i> couldn't be created.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="414"/>
|
||||
<source>A new password list has been created.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="418"/>
|
||||
<source>The password list "%1" has been load.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="481"/>
|
||||
<source>Select where you want to save the password list</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="483"/>
|
||||
<source>All files (*.*)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="485"/>
|
||||
<source>The file was not be saved.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="506"/>
|
||||
<source>There is no password list opened.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="519"/>
|
||||
<source>There's no account selected.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="536"/>
|
||||
<source>The password file has been modified.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="537"/>
|
||||
<source>Do you want to save the changes before closing?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="557"/>
|
||||
<source>The password list has been closed.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="580"/>
|
||||
<source>The backup file couldn't be created. %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="595"/>
|
||||
<source>Enter a password to save the file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="603"/>
|
||||
<source>The file hasn't been saved.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="612"/>
|
||||
<source>The password list couldn't be saved due to encryption failure.
|
||||
OpenSSL error queue: %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="624"/>
|
||||
<source>The password list has been saved.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="643"/>
|
||||
<source>The password list couldn't be exported. %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="646"/>
|
||||
<source>The password list has been exported.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="662"/>
|
||||
<source>The currently opened file hasn't been saved yet.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="676"/>
|
||||
<location filename="../gui/mainwindow.cpp" line="856"/>
|
||||
<source>Add account</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="684"/>
|
||||
<location filename="../gui/mainwindow.cpp" line="857"/>
|
||||
<source>Add category</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="702"/>
|
||||
<source>Enter the entry name</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="702"/>
|
||||
<source>new entry</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="711"/>
|
||||
<source>Unable to create new entry.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="714"/>
|
||||
<source>You didn't enter text.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="720"/>
|
||||
<source>No node element selected.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="735"/>
|
||||
<source>Unable to remove the entry.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="738"/>
|
||||
<source>No entry selected.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="776"/>
|
||||
<source>A field has to be selected since new fields are always inserted before the currently selected field.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="800"/>
|
||||
<source>No fields have been removed since there are currently no fields selected.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="821"/>
|
||||
<source>You didn't enter a password. <strong>No encryption</strong> will be used when saving the file next time.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="825"/>
|
||||
<source>The new password will be used next time you save the file.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="829"/>
|
||||
<source>You aborted. The old password will still be used when saving the file next time.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="858"/>
|
||||
<source>Remove entry</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="872"/>
|
||||
<source>Remove field</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="874"/>
|
||||
<source>Copy for 5 seconds</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="893"/>
|
||||
<source>The selected cell is empty.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/mainwindow.cpp" line="896"/>
|
||||
<source>Exactly one cell needs to be selected.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtGui::PasswordGeneratorDialog</name>
|
||||
<message>
|
||||
<location filename="../gui/passwordgeneratordialog.ui" line="32"/>
|
||||
<source>Password Generator</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/passwordgeneratordialog.ui" line="117"/>
|
||||
<source>Use small letters</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/passwordgeneratordialog.ui" line="127"/>
|
||||
<source>Use capital letters</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/passwordgeneratordialog.ui" line="137"/>
|
||||
<source>Use digits</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/passwordgeneratordialog.ui" line="174"/>
|
||||
<source>Length</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/passwordgeneratordialog.ui" line="181"/>
|
||||
<source>Other characters to be used</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/passwordgeneratordialog.ui" line="188"/>
|
||||
<source>!"§$%&/()=?;:_'*~’#,.-+<>|</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/passwordgeneratordialog.ui" line="229"/>
|
||||
<source><b>Password:</b></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/passwordgeneratordialog.ui" line="248"/>
|
||||
<source>Generate new password</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/passwordgeneratordialog.ui" line="264"/>
|
||||
<source>Copy password</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/passwordgeneratordialog.ui" line="311"/>
|
||||
<source>Close</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/passwordgeneratordialog.cpp" line="130"/>
|
||||
<source>Failed to generate password.
|
||||
OpenSSL error: %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/passwordgeneratordialog.cpp" line="133"/>
|
||||
<source>You have to select at least one checkbox.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/passwordgeneratordialog.cpp" line="136"/>
|
||||
<source>The length has to be at least one.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>undocommands</name>
|
||||
<message>
|
||||
<location filename="../gui/undocommands.cpp" line="76"/>
|
||||
<source>setting field name to »%1«</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/undocommands.cpp" line="78"/>
|
||||
<source>setting field name »%1« to »%2«</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/undocommands.cpp" line="84"/>
|
||||
<source>setting value of empty field to »%1«</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/undocommands.cpp" line="86"/>
|
||||
<source>setting value of »%1« field to »%2«</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="../gui/undocommands.cpp" line="117"/>
|
||||
<source>insertion of %1 row(s) before row %2</source>
|
||||
<translation type="unfinished">
|
||||
<numerusform>insertion of %1 row before row %2</numerusform>
|
||||
<numerusform>insertion of %1 rows before row %2</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="../gui/undocommands.cpp" line="145"/>
|
||||
<source>removal of row %1</source>
|
||||
<translation type="unfinished">
|
||||
<numerusform></numerusform>
|
||||
<numerusform></numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="../gui/undocommands.cpp" line="147"/>
|
||||
<source>removal of the rows %1 to %2</source>
|
||||
<translation type="unfinished">
|
||||
<numerusform></numerusform>
|
||||
<numerusform></numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/undocommands.cpp" line="188"/>
|
||||
<source>selection of account »%1«</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/undocommands.cpp" line="190"/>
|
||||
<source>account selection repealed</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/undocommands.cpp" line="254"/>
|
||||
<source>setting entry name to »%1«</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/undocommands.cpp" line="256"/>
|
||||
<source>setting entry name from »%1« to »%2«</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="../gui/undocommands.cpp" line="297"/>
|
||||
<source>insertion of %1 entry/entries</source>
|
||||
<translation type="unfinished">
|
||||
<numerusform>insertion of %1 entry</numerusform>
|
||||
<numerusform>insertion of %1 entries</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="../gui/undocommands.cpp" line="332"/>
|
||||
<source>removal of %1 entry/entries</source>
|
||||
<translation type="unfinished">
|
||||
<numerusform>removal of %1 entry</numerusform>
|
||||
<numerusform>removal of %1 entries</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="../gui/undocommands.cpp" line="372"/>
|
||||
<source>move of %1 entry/entries</source>
|
||||
<translation type="unfinished">
|
||||
<numerusform>move of %1 entry</numerusform>
|
||||
<numerusform>move of %1 entries</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
|
@ -0,0 +1,37 @@
|
|||
#include "testroutines.h"
|
||||
|
||||
#include <c++utilities/io/binaryreader.h>
|
||||
#include <c++utilities/io/binarywriter.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
using namespace IoUtilities;
|
||||
|
||||
namespace Testroutines {
|
||||
|
||||
void lengthPrefixedString()
|
||||
{
|
||||
stringstream stream;
|
||||
BinaryReader reader(&stream);
|
||||
BinaryWriter writer(&stream);
|
||||
string string1("jöalfj32öl4fj34 f234ölf3je frasdölkajwe fqwöejkfwöfklja sdefölasje fasef jasöefjas efajs eflasje faöslefj asöflej asefölajsefl öasejföaslefja söef jaseö flajseflas jeföaslefj aslefjaweöflja4 rfq34jqlök4jfq ljase öfaje4fqp 34f89uj <pfj apefjawepfoi jaefoaje föasdjfaösefj a4jfase9fau sejfpas");
|
||||
string string2;
|
||||
string string3("asdfalsjd23öl4j3");
|
||||
writer.writeLengthPrefixedString(string1);
|
||||
writer.writeLengthPrefixedString(string2);
|
||||
writer.writeLengthPrefixedString(string3);
|
||||
if(reader.readLengthPrefixedString() == string1
|
||||
&& reader.readLengthPrefixedString() == string2
|
||||
&& reader.readLengthPrefixedString() == string3) {
|
||||
cout << "test sucessfull" << endl;
|
||||
} else {
|
||||
cout << "test failed" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef TESTROUTINES_H
|
||||
#define TESTROUTINES_H
|
||||
|
||||
namespace Testroutines {
|
||||
|
||||
void lengthPrefixedString();
|
||||
|
||||
}
|
||||
|
||||
#endif // TESTROUTINES_H
|