#include "istakip_gantt.hpp" #include #include #include #include #include "fiko/istakip/istemci.hpp" #include "fiko/istakip/model.hpp" #include "imgui.h" #include "istakip_sema.hpp" namespace fiko { namespace { using istakip::IsKayit; // Howard Hinnant'ın civil_from/days_from_civil algoritması (1970-01-01 = 0). long gunSayisiHesapla(int y, unsigned m, unsigned d) { y -= (m <= 2) ? 1 : 0; const long era = (y >= 0 ? y : y - 399) / 400; const unsigned yoe = (unsigned)(y - era * 400); const unsigned doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1; const unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; return era * 146097L + (long)doe - 719468L; } // "YYYY-MM-DD..." önekini çözer (form serbest metin — sıkı doğrulama yok, F1 // kapsamı: kullanıcıdan takvim widget'ı yok, ISO benzeri yazması bekleniyor). bool tarihGunSayisi(const char* s, long& gun) { if (!s || !s[0]) return false; int y, m, d; if (std::sscanf(s, "%d-%d-%d", &y, &m, &d) != 3) return false; if (m < 1 || m > 12 || d < 1 || d > 31) return false; gun = gunSayisiHesapla(y, (unsigned)m, (unsigned)d); return true; } long bugunGunSayisi() { const std::time_t simdi = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); std::tm tmv{}; #if defined(_WIN32) gmtime_s(&tmv, &simdi); #else gmtime_r(&simdi, &tmv); #endif return gunSayisiHesapla(tmv.tm_year + 1900, (unsigned)(tmv.tm_mon + 1), (unsigned)tmv.tm_mday); } } // namespace bool ganttCiz(const istakip::IsIstemci& veri, const std::vector& turler, std::string& hedefTur, uint64_t& hedefKimlik) { struct Oge { const IsKayit* k; long baslangicGun; long terminGun; bool gecikmis; }; struct Serit { const TurSema* t; std::vector ogeler; }; const long bugun = bugunGunSayisi(); long minGun = LONG_MAX, maxGun = LONG_MIN; std::vector seritler; for (const TurSema& t : turler) { bool terminVar = false; for (const AlanSema& a : t.alanlar) if (a.ad == "termin") terminVar = true; if (!terminVar) continue; Serit s{&t, {}}; for (const IsKayit* k : veri.turListe(t.tur_id)) { long g; if (!tarihGunSayisi(istakip::alanBul(*k, "termin"), g)) continue; long bg = g; long bb; if (tarihGunSayisi(istakip::alanBul(*k, "baslangic"), bb)) bg = bb; bool tamam = false; if (!t.durumlar.empty()) { const char* d = istakip::alanBul(*k, "durum"); if (d && t.durumlar.back() == d) tamam = true; } s.ogeler.push_back({k, bg, g, !tamam && g < bugun}); minGun = std::min({minGun, bg, g}); maxGun = std::max(maxGun, g); } if (!s.ogeler.empty()) seritler.push_back(std::move(s)); } if (seritler.empty()) { ImGui::TextDisabled("Zaman çizelgesi için 'termin' alanlı ve tarihi girilmiş kayıt yok."); ImGui::TextDisabled("Tarih biçimi: YYYY-AA-GG (örn: 2026-09-15)"); return false; } minGun -= 1; maxGun += 1; minGun = std::min(minGun, bugun - 1); maxGun = std::max(maxGun, bugun + 1); const float etiketGen = 140.0f; const float genislik = std::max(400.0f, ImGui::GetContentRegionAvail().x - etiketGen - 24.0f); const float pxPerGun = genislik / (float)std::max(1L, maxGun - minGun); const float satirYuk = 32.0f; bool tiklandi = false; ImGui::BeginChild("##gantt", {0, 0}, ImGuiChildFlags_Borders, ImGuiWindowFlags_HorizontalScrollbar); ImDrawList* dl = ImGui::GetWindowDrawList(); const ImVec2 orijin = ImGui::GetCursorScreenPos(); const float toplamYuk = (float)seritler.size() * satirYuk + 24.0f; // Bugün çizgisi. const float bugunX = orijin.x + etiketGen + (float)(bugun - minGun) * pxPerGun; dl->AddLine({bugunX, orijin.y}, {bugunX, orijin.y + toplamYuk}, IM_COL32(230, 90, 90, 150), 1.5f); dl->AddText({bugunX + 3, orijin.y}, IM_COL32(230, 90, 90, 220), "bugün"); float y = orijin.y + 20.0f; for (const Serit& s : seritler) { char baslikBuf[80]; std::snprintf(baslikBuf, sizeof(baslikBuf), "%s %s", s.t->ikon.c_str(), s.t->baslik.c_str()); dl->AddText({orijin.x, y + 8}, IM_COL32(210, 210, 215, 255), baslikBuf); for (const Oge& o : s.ogeler) { const float x0 = orijin.x + etiketGen + (float)(o.baslangicGun - minGun) * pxPerGun; const float x1 = orijin.x + etiketGen + (float)(o.terminGun - minGun) * pxPerGun; const float xx1 = std::max(x1, x0 + 6.0f); const ImVec2 p0{x0, y + 2}, p1{xx1, y + 24}; const bool hover = ImGui::IsMouseHoveringRect(p0, p1); const ImU32 renk = o.gecikmis ? IM_COL32(214, 90, 90, hover ? 255 : 210) : IM_COL32(76, 155, 232, hover ? 255 : 200); dl->AddRectFilled(p0, p1, renk, 3.0f); if (hover) { const char* ilkAlan = s.t->alanlar.empty() ? "" : istakip::alanBul(*o.k, s.t->alanlar[0].ad.c_str()); ImGui::SetTooltip("%s\ntermin: %s", (ilkAlan && ilkAlan[0]) ? ilkAlan : "(başlıksız)", istakip::alanBul(*o.k, "termin")); if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { hedefTur = s.t->tur_id; hedefKimlik = o.k->kimlik; tiklandi = true; } } } y += satirYuk; } ImGui::Dummy({etiketGen + genislik, toplamYuk}); ImGui::EndChild(); return tiklandi; } } // namespace fiko