333 lines
9.7 KiB
C++
333 lines
9.7 KiB
C++
// ============================================
|
|
// Final-Projekt: Todo-App in C++
|
|
// Moderne C++17 Implementierung mit STL
|
|
// ============================================
|
|
|
|
#include <iostream>
|
|
#include <vector>
|
|
#include <string>
|
|
#include <ctime>
|
|
#include <iomanip>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <algorithm>
|
|
|
|
// ============================================
|
|
// KLASSE: Todo
|
|
// ============================================
|
|
class Todo {
|
|
private:
|
|
int id_;
|
|
std::string title_;
|
|
std::string description_;
|
|
bool completed_;
|
|
std::time_t created_at_;
|
|
|
|
public:
|
|
// Konstruktor
|
|
Todo(int id, std::string title, std::string description)
|
|
: id_(id)
|
|
, title_(std::move(title))
|
|
, description_(std::move(description))
|
|
, completed_(false)
|
|
, created_at_(std::time(nullptr)) {}
|
|
|
|
// Getter
|
|
int id() const { return id_; }
|
|
const std::string& title() const { return title_; }
|
|
const std::string& description() const { return description_; }
|
|
bool completed() const { return completed_; }
|
|
std::time_t created_at() const { return created_at_; }
|
|
|
|
// Setter
|
|
void toggle_completed() { completed_ = !completed_; }
|
|
void set_title(const std::string& title) { title_ = title; }
|
|
void set_description(const std::string& desc) { description_ = desc; }
|
|
|
|
// Formatierung für Ausgabe
|
|
std::string to_string() const {
|
|
std::stringstream ss;
|
|
ss << "#" << id_ << " [" << (completed_ ? "✓" : " ") << "] " << title_;
|
|
return ss.str();
|
|
}
|
|
|
|
// Serialisierung für Datei-Speicherung
|
|
std::string serialize() const {
|
|
std::stringstream ss;
|
|
ss << id_ << "|" << title_ << "|" << description_ << "|"
|
|
<< (completed_ ? "1" : "0") << "|" << created_at_;
|
|
return ss.str();
|
|
}
|
|
|
|
static Todo deserialize(const std::string& line) {
|
|
std::stringstream ss(line);
|
|
std::string part;
|
|
std::vector<std::string> parts;
|
|
|
|
while (std::getline(ss, part, '|')) {
|
|
parts.push_back(part);
|
|
}
|
|
|
|
Todo todo(std::stoi(parts[0]), parts[1], parts[2]);
|
|
if (parts[3] == "1") todo.toggle_completed();
|
|
return todo;
|
|
}
|
|
};
|
|
|
|
// ============================================
|
|
// KLASSE: TodoManager
|
|
// ============================================
|
|
class TodoManager {
|
|
private:
|
|
std::vector<Todo> todos_;
|
|
int next_id_ = 1;
|
|
const std::string filename_ = "todos_cpp.txt";
|
|
|
|
public:
|
|
// CRUD Operationen
|
|
void add(const std::string& title, const std::string& description) {
|
|
todos_.emplace_back(next_id_++, title, description);
|
|
std::cout << "✓ Todo hinzugefügt!\n";
|
|
}
|
|
|
|
void list() const {
|
|
if (todos_.empty()) {
|
|
std::cout << "Keine Todos vorhanden.\n";
|
|
return;
|
|
}
|
|
|
|
std::cout << "\n=== DEINE TODOS ===\n";
|
|
for (const auto& todo : todos_) {
|
|
std::cout << todo.to_string() << "\n";
|
|
if (!todo.description().empty()) {
|
|
std::cout << " " << todo.description() << "\n";
|
|
}
|
|
}
|
|
std::cout << "\n";
|
|
}
|
|
|
|
void complete(int id) {
|
|
auto it = find_by_id(id);
|
|
if (it != todos_.end()) {
|
|
it->toggle_completed();
|
|
std::cout << "✓ Todo #" << id << (it->completed() ? " abgeschlossen" : " wieder geöffnet") << "\n";
|
|
} else {
|
|
std::cout << "✗ Todo #" << id << " nicht gefunden!\n";
|
|
}
|
|
}
|
|
|
|
void remove(int id) {
|
|
auto it = find_by_id(id);
|
|
if (it != todos_.end()) {
|
|
todos_.erase(it);
|
|
// IDs neu zuweisen
|
|
for (size_t i = 0; i < todos_.size(); ++i) {
|
|
// Note: In echtem Code würde man IDs nicht ändern
|
|
}
|
|
std::cout << "✓ Todo #" << id << " gelöscht!\n";
|
|
} else {
|
|
std::cout << "✗ Todo #" << id << " nicht gefunden!\n";
|
|
}
|
|
}
|
|
|
|
void edit(int id, const std::string& new_title) {
|
|
auto it = find_by_id(id);
|
|
if (it != todos_.end()) {
|
|
it->set_title(new_title);
|
|
std::cout << "✓ Todo #" << id << " aktualisiert!\n";
|
|
} else {
|
|
std::cout << "✗ Todo #" << id << " nicht gefunden!\n";
|
|
}
|
|
}
|
|
|
|
// Filter-Funktionen
|
|
void list_open() const {
|
|
std::cout << "\n=== OFFENE TODOS ===\n";
|
|
for (const auto& todo : todos_) {
|
|
if (!todo.completed()) {
|
|
std::cout << todo.to_string() << "\n";
|
|
}
|
|
}
|
|
std::cout << "\n";
|
|
}
|
|
|
|
void list_completed() const {
|
|
std::cout << "\n=== ABGESCHLOSSENE TODOS ===\n";
|
|
for (const auto& todo : todos_) {
|
|
if (todo.completed()) {
|
|
std::cout << todo.to_string() << "\n";
|
|
}
|
|
}
|
|
std::cout << "\n";
|
|
}
|
|
|
|
void search(const std::string& keyword) const {
|
|
std::cout << "\n=== SUCHERGEBNISSE ===\n";
|
|
for (const auto& todo : todos_) {
|
|
if (todo.title().find(keyword) != std::string::npos ||
|
|
todo.description().find(keyword) != std::string::npos) {
|
|
std::cout << todo.to_string() << "\n";
|
|
}
|
|
}
|
|
std::cout << "\n";
|
|
}
|
|
|
|
// Datei-Operationen
|
|
void save() const {
|
|
std::ofstream file(filename_);
|
|
if (!file) {
|
|
std::cout << "✗ Fehler beim Speichern!\n";
|
|
return;
|
|
}
|
|
|
|
for (const auto& todo : todos_) {
|
|
file << todo.serialize() << "\n";
|
|
}
|
|
|
|
std::cout << "✓ " << todos_.size() << " Todos gespeichert!\n";
|
|
}
|
|
|
|
void load() {
|
|
std::ifstream file(filename_);
|
|
if (!file) {
|
|
std::cout << "Keine vorhandenen Daten. Starte neu...\n";
|
|
return;
|
|
}
|
|
|
|
todos_.clear();
|
|
std::string line;
|
|
while (std::getline(file, line)) {
|
|
if (!line.empty()) {
|
|
todos_.push_back(Todo::deserialize(line));
|
|
}
|
|
}
|
|
|
|
// next_id aktualisieren
|
|
if (!todos_.empty()) {
|
|
next_id_ = std::max_element(todos_.begin(), todos_.end(),
|
|
[](const Todo& a, const Todo& b) { return a.id() < b.id(); })->id() + 1;
|
|
}
|
|
|
|
std::cout << "✓ " << todos_.size() << " Todos geladen!\n";
|
|
}
|
|
|
|
private:
|
|
std::vector<Todo>::iterator find_by_id(int id) {
|
|
return std::find_if(todos_.begin(), todos_.end(),
|
|
[id](const Todo& t) { return t.id() == id; });
|
|
}
|
|
};
|
|
|
|
// ============================================
|
|
// UI-FUNKTIONEN
|
|
// ============================================
|
|
void print_menu() {
|
|
std::cout << "\n=== TODO-APP (C++ Edition) ===\n";
|
|
std::cout << "1. Todo hinzufügen\n";
|
|
std::cout << "2. Alle Todos anzeigen\n";
|
|
std::cout << "3. Offene Todos anzeigen\n";
|
|
std::cout << "4. Abgeschlossene Todos anzeigen\n";
|
|
std::cout << "5. Todo abschließen/öffnen\n";
|
|
std::cout << "6. Todo bearbeiten\n";
|
|
std::cout << "7. Todo löschen\n";
|
|
std::cout << "8. Suchen\n";
|
|
std::cout << "9. Speichern\n";
|
|
std::cout << "0. Beenden\n";
|
|
std::cout << "Wahl: ";
|
|
}
|
|
|
|
// ============================================
|
|
// HAUPTPROGRAMM
|
|
// ============================================
|
|
int main() {
|
|
TodoManager manager;
|
|
manager.load();
|
|
|
|
std::cout << "\n📝 Willkommen zur Todo-App (C++17)!\n";
|
|
|
|
int choice;
|
|
std::string title, description, keyword;
|
|
int id;
|
|
|
|
while (true) {
|
|
print_menu();
|
|
|
|
if (!(std::cin >> choice)) {
|
|
std::cin.clear();
|
|
std::cin.ignore(10000, '\n');
|
|
std::cout << "Ungültige Eingabe!\n";
|
|
continue;
|
|
}
|
|
std::cin.ignore(10000, '\n'); // Rest der Zeile ignorieren
|
|
|
|
switch (choice) {
|
|
case 1:
|
|
std::cout << "Titel: ";
|
|
std::getline(std::cin, title);
|
|
std::cout << "Beschreibung: ";
|
|
std::getline(std::cin, description);
|
|
manager.add(title, description);
|
|
break;
|
|
|
|
case 2:
|
|
manager.list();
|
|
break;
|
|
|
|
case 3:
|
|
manager.list_open();
|
|
break;
|
|
|
|
case 4:
|
|
manager.list_completed();
|
|
break;
|
|
|
|
case 5:
|
|
std::cout << "Todo-ID: ";
|
|
std::cin >> id;
|
|
manager.complete(id);
|
|
break;
|
|
|
|
case 6:
|
|
std::cout << "Todo-ID: ";
|
|
std::cin >> id;
|
|
std::cin.ignore(10000, '\n');
|
|
std::cout << "Neuer Titel: ";
|
|
std::getline(std::cin, title);
|
|
manager.edit(id, title);
|
|
break;
|
|
|
|
case 7:
|
|
std::cout << "Todo-ID: ";
|
|
std::cin >> id;
|
|
manager.remove(id);
|
|
break;
|
|
|
|
case 8:
|
|
std::cout << "Suchbegriff: ";
|
|
std::getline(std::cin, keyword);
|
|
manager.search(keyword);
|
|
break;
|
|
|
|
case 9:
|
|
manager.save();
|
|
break;
|
|
|
|
case 0: {
|
|
std::cout << "Speichern vor Beenden? (j/n): ";
|
|
char save_choice;
|
|
std::cin >> save_choice;
|
|
if (save_choice == 'j' || save_choice == 'J') {
|
|
manager.save();
|
|
}
|
|
std::cout << "Auf Wiedersehen!\n";
|
|
return 0;
|
|
}
|
|
|
|
default:
|
|
std::cout << "Ungültige Wahl!\n";
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|