diff --git a/include/lgi/common/Store3.h b/include/lgi/common/Store3.h --- a/include/lgi/common/Store3.h +++ b/include/lgi/common/Store3.h @@ -1,720 +1,722 @@ /// \file /// \author Matthew Allen, fret@memecode.com #ifndef _MAIL_STORE_H_ #define _MAIL_STORE_H_ #include #include "Mail.h" #include "Store3Defs.h" #undef GetObject /* Handling of attachments in the Store3 API ----------------------------------------- Given the mail object ptr: LDataI *m; Query that the mail for it's root mime segment: LDataI *Seg = dynamic_cast(m->GetObj(FIELD_MIME_SEG)); Query a seg for it's children: GDataIt Children = Seg->GetList(FIELD_MIME_SEG); LDataI *FirstChild = Children->First(); Access segment's charset and mimetype: char *Charset = Seg->GetStr(FIELD_CHARSET); char *MimeType = Seg->GetStr(FIELD_MIME_TYPE); Get/Set the segment for it's content: GAutoStreamI Data = Seg->GetStream(_FL); // get Seg->SetStream(new MyStream(Data)); // set Delete an mime segment: Seg->Delete(); Add a new segment somewhere in the tree, including reparenting it to another segments or mail object, even if the target parent it not attached yet: NewSeg->Save(ParentSeg); */ #include "LgiInterfaces.h" #include "lgi/common/Mime.h" #include "lgi/common/OptionsFile.h" #include "lgi/common/Variant.h" class LDataI; class LDataFolderI; class LDataStoreI; class LDataPropI; typedef LAutoPtr LAutoStreamI; void ParseIdList(char *In, List &Out); extern const char *Store3ItemTypeToMime(Store3ItemTypes type); /// A storage event /// a = StoreId /// b = (void*)UserParam /// \sa LDataEventsI::Post #define M_STORAGE_EVENT (M_USER+0x500) /// The storage class has this property (positive properties are owned by the app #define FIELD_IS_ONLINE -100 #define FIELD_PROFILE_IMAP_LISTING -101 #define FIELD_PROFILE_IMAP_SELECT -102 #define LDATA_INT32_PROP(name, id) \ int32 Get##name() { return GetObject() ? (int32)GetObject()->GetInt(id) : OnError(_FL); } \ bool Set##name(int32 val) { return GetObject() ? GetObject()->SetInt(id, val) >= Store3Delayed : OnError(_FL); } #define LDATA_INT64_PROP(name, id) \ int64 Get##name() { return GetObject() ? GetObject()->GetInt(id) : OnError(_FL); } \ bool Set##name(int64 val) { return GetObject() ? GetObject()->SetInt(id, val) >= Store3Delayed : OnError(_FL); } #define LDATA_ENUM_PROP(name, id, type) \ type Get##name() { return (type) (GetObject() ? GetObject()->GetInt(id) : OnError(_FL)); } \ bool Set##name(type val) { return GetObject() ? GetObject()->SetInt(id, (int)val) >= Store3Delayed : OnError(_FL); } #define LDATA_STR_PROP(name, id) \ const char *Get##name() { auto o = GetObject(); return o ? o->GetStr(id) : (const char*)OnError(_FL); } \ bool Set##name(const char *val) { return GetObject() ? GetObject()->SetStr(id, val) >= Store3Delayed : OnError(_FL); } #define LDATA_DATE_PROP(name, id) \ const LDateTime *Get##name() { return (GetObject() ? GetObject()->GetDate(id) : (LDateTime*)OnError(_FL)); } \ bool Set##name(const LDateTime *val) { return GetObject() ? GetObject()->SetDate(id, val) >= Store3Delayed : OnError(_FL); } #define LDATA_INT_TYPE_PROP(type, name, id, defaultVal) \ type Get##name() { return (type) (GetObject() ? GetObject()->GetInt(id) : OnError(_FL)); } \ bool Set##name(type val = defaultVal) { return GetObject() ? GetObject()->SetInt(id, val) >= Store3Delayed : OnError(_FL); } /// This class is an interface to a collection of objects (NOT thread-safe). typedef std::function LIteratorProgressFn; template class LDataIterator { public: virtual ~LDataIterator() {} /// \returns an empty object of the right type. virtual T Create(LDataStoreI *Store) = 0; /// \returns the first object (NOT thread-safe) virtual T First() = 0; /// \returns the first object (NOT thread-safe) virtual T Next() = 0; /// \returns the number of items in the collection virtual size_t Length() = 0; /// \returns the 'nth' item in the collection virtual T operator [](size_t idx) = 0; /// \returns the index of the given item in the collection virtual ssize_t IndexOf(T n, bool NoAssert = false) = 0; /// Deletes an item /// \returns true on success virtual bool Delete(T ptr) = 0; /// Inserts an item at 'idx' or the end if not supplied. /// \returns true on success virtual bool Insert(T ptr, ssize_t idx = -1, bool NoAssert = false) = 0; /// Clears list, but doesn't delete objects. /// \returns true on success virtual bool Empty() = 0; /// Deletes all the objects from memory /// \returns true on success virtual bool DeleteObjects() = 0; /// Gets the current loading/loaded state. virtual Store3State GetState() = 0; /// Sets the progress function virtual void SetProgressFn(LIteratorProgressFn cb) = 0; }; typedef LDataIterator *GDataIt; #define EmptyVirtual(t) LAssert(0); return t #define Store3CopyDecl bool CopyProps(LDataPropI &p) override #define Store3CopyImpl(Cls) bool Cls::CopyProps(LDataPropI &p) /// A generic interface for getting / setting properties. class LDataPropI : virtual public LDom { LDataPropI &operator =(LDataPropI &p) = delete; public: virtual ~LDataPropI() {} /// Copy all the values from 'p' over to this object virtual bool CopyProps(LDataPropI &p) { return false; } /// Gets a string property virtual const char *GetStr(int id) { EmptyVirtual(NULL); } /// Sets a string property, it will make a copy of the string, so you /// still retain ownership of the string you're passing in. virtual Store3Status SetStr(int id, const char *str) { EmptyVirtual(Store3Error); } /// Gets an integer property. virtual int64 GetInt(int id) { EmptyVirtual(false); } /// Sets an integer property. virtual Store3Status SetInt(int id, int64 i) { EmptyVirtual(Store3Error); } /// Gets a date property virtual const LDateTime *GetDate(int id) { EmptyVirtual(NULL); } /// Sets a date property virtual Store3Status SetDate(int id, const LDateTime *i) { EmptyVirtual(Store3Error); } /// Gets a variant virtual const LVariant *GetVar(int id) { EmptyVirtual(NULL); } /// Sets a variant property virtual Store3Status SetVar(int id, LVariant *i) { EmptyVirtual(Store3Error); } /// Gets a sub object pointer virtual LDataPropI *GetObj(int id) { EmptyVirtual(NULL); } /// Sets a sub object pointer virtual Store3Status SetObj(int id, LDataPropI *i) { EmptyVirtual(Store3Error); } /// Gets an iterator interface to a list of sub-objects. virtual GDataIt GetList(int id) { EmptyVirtual(NULL); } /// Set the mime segments virtual Store3Status SetRfc822(LStreamI *Rfc822Msg) { LAssert(!"Pretty sure you should be implementing this") ; return Store3Error; } }; #pragma warning(default:4263) class LDataUserI { friend class LDataI; LDataI *Object; public: LString SetterRef; LDataUserI(); virtual ~LDataUserI(); LDataI *GetObject(); virtual bool SetObject ( /// The client side object to link with this object. LDataI *o, /// In the special case that 'Object' is being deleted, and is an /// orphaned objects, SetObject must not attempt to delete 'Object' /// a second time. This flag allows for that case. bool InDestuctor, /// The file name of the caller const char *File, /// The line number of the caller int Line ); }; /// This class is an interface between the UI and the backend for things /// like email, contacts, calendar events, groups and filters class LDataI : virtual public LDataPropI { friend class LDataUserI; LDataI &operator =(LDataI &p) = delete; public: LDataUserI *UserData; LDataI() { UserData = NULL; } virtual ~LDataI() { if (UserData) UserData->Object = NULL; } /// Returns the type of object /// \sa MAGIC_MAIL and it's like virtual uint32_t Type() = 0; /// \return true if the object has been written to disk. By default the object /// starts life in memory only. virtual bool IsOnDisk() = 0; /// \return true if the object is owned by some other object... virtual bool IsOrphan() = 0; /// \returns size of object on disk virtual uint64 Size() = 0; /// Saves the object to disk. If this function fails the object /// is deleted, so if it returns false, stop using the ptr you /// have to it. /// \returns true if successful. virtual Store3Status Save(LDataI *Parent = 0) = 0; /// Delete the on disk representation of the object. This will cause LDataEventsI::OnDelete /// to be called after which this object will be freed from heap memory automatically. So /// Once you call this method assume the object pointed at is gone. virtual Store3Status Delete(bool ToTrash = true) = 0; /// Gets the storage that this object belongs to. virtual LDataStoreI *GetStore() = 0; /// \returns a stream to access the data stored at this node. The caller /// is responsible to free the stream when finished with it. /// For Type == MAGIC_ATTACHMENT: the decoded body of the MIME segment. /// For Type == MAGIC_MAIL: is an RFC822 encoded version of the email. /// For other objects the stream is not defined. virtual LAutoStreamI GetStream(const char *file, int line) = 0; /// Sets the stream, which is used during the next call to LDataI::Save, which /// also deletes the object when it's used. The caller loses ownership of the /// object passed into this function. virtual bool SetStream(LAutoStreamI stream) { return false; } /// Parses the headers of the object and updates all the metadata fields virtual bool ParseHeaders() { return false; } }; /// An interface to a folder structure class LDataFolderI : virtual public LDataI { LDataFolderI &operator =(LDataFolderI &p) = delete; public: virtual ~LDataFolderI() {} /// \returns an iterator for the sub-folders. virtual LDataIterator &SubFolders() = 0; /// \returns an iterator for the child objects virtual LDataIterator &Children() = 0; /// \returns an iterator for the fields this folder defines virtual LDataIterator &Fields() = 0; /// Deletes all child objects from disk and memory. /// \return true on success; virtual Store3Status DeleteAllChildren() { return Store3Error; } /// Frees all the memory used by children objects without deleting from disk virtual Store3Status FreeChildren() { return Store3Error; } /// Called when the user selects the folder in the UI virtual void OnSelect(bool s) {} /// Called when the user selects a relevant context menu command virtual void OnCommand(const char *Name) {} }; #pragma warning(error:4263) /// Event callback interface. Calls to these methods may be in a worker /// thread, so make appropriate locking or pass the event off to the GUI /// thread via a message. class LDataEventsI { public: virtual ~LDataEventsI() {} /// This allows the caller to pass source:line info for debugging /// It should be called prior to one of the following functions and /// expires immediately after the function call. virtual void SetContext(const char *file, int line) {} /// Posts something to the GUI thread /// \sa M_STORAGE_EVENT virtual void Post(LDataStoreI *store, void *Param) {} /// \returns the system path virtual bool GetSystemPath(int Folder, LVariant &Path) { return false; } /// \returns the options object virtual LOptionsFile *GetOptions(bool Create = false) { return 0; } /// A new item is available virtual void OnNew(LDataFolderI *parent, LArray &new_items, int pos, bool is_new) = 0; /// When an item is deleted virtual bool OnDelete(LDataFolderI *parent, LArray &items) = 0; /// When an item is moved to a new folder virtual bool OnMove(LDataFolderI *new_parent, LDataFolderI *old_parent, LArray &items) = 0; /// When an item changes virtual bool OnChange(LArray &items, int FieldHint) = 0; /// Notifcation of property change virtual void OnPropChange(LDataStoreI *Store, int Prop, LVariantType Type) {} /// Get the logging stream virtual LStreamI *GetLogger(LDataStoreI *store) { return 0; } /// Search for a object by type and name virtual bool Match(LDataStoreI *store, LDataPropI *Addr, int ObjectType, LArray &Matches) { return 0; } }; /// The virtual mail storage interface from which all mail stores inherit from. /// /// The data store should implement LDataPropI::GetInt and handle these properties: /// - FIELD_STATUS, acceptable returns value are: /// * 0 - mail store is in error. /// * 1 - mail store is ready to use / ok. /// * 2 - mail store requires upgrading to use. /// These are codified in the enum LDataStoreI::DataStoreStatus /// - FIELD_READONLY, return values are: /// * false - mail store is read/write /// * true - mail store is read only /// - FIELD_VERSION, existing return values are: /// * 3 - a 'mail3' Scribe sqlite database. /// * 10 - the Scribe IMAP implementation. /// If you are create a new mail store use, values above 10 for your implementation /// and optionally register them with Memecode for inclusion here. /// - FIELD_IS_ONLINE, optionally returned if mail store is online or not. /// - FIELD_ACCOUNT_ID, optionally return if the mail store is associated with an account. /// /// The data store may optionally implement LDataPropI::SetInt to handle this property: /// - FIELD_IS_ONLINE, acceptable values are: /// * false - take the mail store offline. /// * true - go online. /// This is currently only implemented on the IMAP mail store. class LDataStoreI : virtual public LDataPropI { public: static LHashTbl,LDataStoreI*> Map; int Id; class LDsTransaction { protected: LDataStoreI *Store; public: LDsTransaction(LDataStoreI *s) { Store = s; } virtual ~LDsTransaction() {} }; typedef LAutoPtr StoreTrans; LDataStoreI() { LAssert(LAppInst->InThread()); while (Map.Find(Id = LRand(1000))) ; Map.Add(Id, this); } virtual ~LDataStoreI() { LAssert(LAppInst->InThread()); if (!Map.Delete(Id)) LAssert(!"Delete failed."); } /// \returns size of object on disk virtual uint64 Size() = 0; /// Create a new data object that isn't written to disk yet virtual LDataI *Create(int Type) = 0; /// Get the root folder object virtual LDataFolderI *GetRoot(bool create = false) = 0; /// Move objects into a different folder. /// /// Success: /// 'Items' are owned by 'NewFolder', and not any previous folder. /// Any LDataEventsI interface owned by the data store has it's 'OnMove' method called. /// /// Failure: /// 'Item' is owned by it's previous folder. /// /// \return true on success. virtual Store3Status Move ( /// The folder to move the object to LDataFolderI *NewFolder, /// The object to move LArray &Items ) = 0; /// Deletes items, which results in either the items being moved to the local trash folder /// or the items being completely deleted if there is no local trash. The items should all /// be in the same folder and of the same type. /// /// Success: /// 'Items' are either owned by the local trash and not any previous folder, and /// LDataEventsI::OnMove is called. /// -or- /// 'Items' are completely deleted and removed from it's parent and /// LDataEventsI::OnDelete is called, after which the objects are freed. /// /// Failure: /// 'Items' are owned by it's previous folder. /// /// \return true on success. virtual Store3Status Delete ( /// The object to delete LArray &Items, /// Send to the trash or not... bool ToTrash ) = 0; /// Changes items, which results in either the items properties being adjusted. /// /// Success: /// The items properties are changed, and the LDataEventsI::OnChange callback /// is made. /// /// Failure: /// Items are not changed. No callback is made. /// /// \return true on success. virtual Store3Status Change ( /// The object to change LArray &Items, /// The property to change... int PropId, /// The value to assign /// (GV_INT32/64 -> SetInt, GV_DATETIME -> SetDateTime, GV_STRING -> SetStr) LVariant &Value, /// Optional operator for action LOperator Operator ) = 0; /// Compact the mail store - virtual bool Compact + virtual void Compact ( /// The parent window of the UI LViewI *Parent, /// The store should pass information up to the UI via setting various parameters from Store3UiFields - LDataPropI *Props + LDataPropI *Props, + /// The callback to get status, could be called by a worker thread... + std::function OnStatus ) = 0; /// Upgrades the mail store to the current version for this build. You should call this in response /// to getting Store3UpgradeRequired back from this->GetInt(FIELD_STATUS). virtual bool Upgrade ( /// The parent window of the UI LViewI *Parent, /// The store should pass information up to the UI via setting various parameters from Store3UiFields LDataPropI *Props ) { return false; } /// Tries to repair the database. virtual bool Repair ( /// The parent window of the UI LViewI *Parent, /// The store should pass information up to the UI via setting various parameters from Store3UiFields LDataPropI *Props ) { return false; } /// Set the sub-format virtual bool SetFormat ( /// The parent window of the UI LViewI *Parent, /// The store should pass information up to the UI via setting various parameters from Store3UiFields LDataPropI *Props ) { return false; } /// Called when event posted virtual void OnEvent(void *Param) = 0; /// Called when the application is not receiving messages. /// \returns false to wait for more messages. virtual bool OnIdle() = 0; /// Gets the events interface virtual LDataEventsI *GetEvents() = 0; /// Start a scoped transaction virtual StoreTrans StartTransaction() { return StoreTrans(0); } }; /// Open a mail3 folder /// \return a valid ptr or NULL on failure extern LDataStoreI *OpenMail3 ( /// The file to open const char *Mail3Folder, /// Event interface, LDataEventsI *Callback, /// true if you want to create a new mail3 file. bool Create = false ); /// Open am imap store /// \return a valid ptr or NULL on failure extern LDataStoreI *OpenImap ( /// The host name of the IMAP server char *Host, /// The port to connect to, or <= 0 means use default int Port, /// The user name of the account to connect to char *User, /// [Optional] The password of the user char *Pass, /// Various flags that control the type of connection made: /// \sa #MAIL_SSL, #MAIL_SECURE_AUTH int ConnectFlags, /// Callback interface for various events... LDataEventsI *Callback, /// This allows the IMAP client to request SSL support from the /// parent applications. LCapabilityClient *caps, /// Pointers to the progress info bars, or NULL if not needed. MailProtocolProgress *prog[2], /// The logging stream. LStream *Log, /// The identifier for the account int AccoundId, /// An interface into the persistant storage area. LAutoPtr store ); #ifdef WIN32 /// Open a MAPI store /// \return a valid ptr or NULL on failure extern LDataStoreI *OpenMapiStore ( /// The MAPI profile name const char *Profile, /// The username to login as const char *Username, /// Their password const char *Password, /// The account ID uint64 AccountId, /// Event interface, LDataEventsI *Callback ); #endif ////////////////////////////////////////////////////////////////////////////// // Common implementation of interfaces template class DNullIterator : public LDataIterator { public: T First() { return 0; } T Next() { return 0; } int Length() { return 0; } T operator [](int idx) { return 0; } bool Delete(T ptr) { return 0; } bool Insert(T ptr, int idx = -1, bool NoAssert = false) { return 0; } bool DeleteObjects() { return 0; } bool Empty() { return false; } int IndexOf(T n, bool NoAssert = false) { return -1; } }; template class DIterator : public LDataIterator { int Cur; public: LArray a; Store3State State; LIteratorProgressFn Prog; DIterator() { Cur = -1; State = Store3Unloaded; } Store3State GetState() { return State; } void SetProgressFn(LIteratorProgressFn cb) { Prog = cb; } void Swap(DIterator &di) { LSwap(Cur, di.Cur); LSwap(State, di.State); a.Swap(di.a); } TPub *Create(LDataStoreI *Store) { LAssert(State == Store3Loaded); return new TPriv(dynamic_cast(Store)); } TPub *First() { LAssert(State == Store3Loaded); Cur = 0; return (int)a.Length() > Cur ? a[Cur] : 0; } TPub *Next() { LAssert(State == Store3Loaded); Cur++; return (int)a.Length() > Cur ? a[Cur] : 0; } size_t Length() { return a.Length(); } TPub *operator [](size_t idx) { LAssert(State == Store3Loaded); return a[idx]; } bool Delete(TPub *pub_ptr) { LAssert(State == Store3Loaded); TPriv *priv_ptr = dynamic_cast(pub_ptr); if (!priv_ptr) { LAssert(!"Not the right type of object."); return false; } ssize_t i = a.IndexOf(priv_ptr); if (i < 0) return false; a.DeleteAt(i, true); return true; } bool Insert(TPub *pub_ptr, ssize_t idx = -1, bool NoAssert = false) { if (!NoAssert) LAssert(State == Store3Loaded); TPriv *priv_ptr = dynamic_cast(pub_ptr); if (!priv_ptr) { LAssert(!"Not the right type of object."); return false; } return a.AddAt(idx < 0 ? a.Length() : idx, priv_ptr); } bool Empty() { LAssert(State == Store3Loaded); a.Length(0); return true; } bool DeleteObjects() { a.DeleteObjects(); return true; } ssize_t IndexOf(TPub *pub_ptr, bool NoAssert = false) { if (!NoAssert) LAssert(State == Store3Loaded); TPriv *priv_ptr = dynamic_cast(pub_ptr); if (!priv_ptr) { LAssert(!"Not the right type of object."); return -1; } return a.IndexOf(priv_ptr); } }; #endif diff --git a/src/common/Widgets/ProgressDlg.cpp b/src/common/Widgets/ProgressDlg.cpp --- a/src/common/Widgets/ProgressDlg.cpp +++ b/src/common/Widgets/ProgressDlg.cpp @@ -1,645 +1,643 @@ /*hdr ** FILE: LProgressDlg.cpp ** AUTHOR: Matthew Allen ** DATE: 11/11/98 ** DESCRIPTION: Progress stuff ** ** Copyright (C) 1998, Matthew Allen ** fret@memecode.com */ #include #include #include "lgi/common/Lgi.h" #include "lgi/common/ProgressDlg.h" #include "lgi/common/TextLabel.h" #include "lgi/common/Button.h" #include "lgi/common/TableLayout.h" #include "lgi/common/LgiRes.h" ///////////////////////////////////////////////////////////////////////////////////////// Progress::Progress() : LMutex("Progress") { } Progress::Progress(char *desc, int64 l, int64 h, char *type, double scale) : LMutex("Progress") { Description = desc; Val = Low = l; High = h; Type = type; Scale = scale; } Progress::~Progress() { } LString Progress::GetDescription() { LString r; LMutex::Auto lck(this, _FL); if (!lck) LAssert(0); else r = Description.Get(); return r; } void Progress::SetDescription(const char *d) { LMutex::Auto lck(this, _FL); if (!lck) { LAssert(0); return; } if (d != Description) Description = d; } LString Progress::GetType() { LString s; { LMutex::Auto lck(this, _FL); if (lck) s = Type.Get(); } return s; } void Progress::SetType(const char *t) { LMutex::Auto lck(this, _FL); Type = t; } Progress &Progress::operator =(Progress &p) { SetDescription(p.GetDescription()); SetRange(p.GetRange()); SetScale(p.GetScale()); SetType(p.GetType()); Value(p.Value()); return *this; } ///////////////////////////////////////////////////////////////////////////////////////// #define IDC_DESCRIPTION 100 #define IDC_VALUE 101 #define IDC_RATE 102 #define IDC_PROGRESS 103 #define IDC_BUTTON 104 #define IDC_TABLE 105 #define IDC_PANE 106 #define IDC_REMAINING 107 #define PANE_X 300 #define PANE_Y 100 LProgressPane::LProgressPane(LProgressDlg *dlg) : Dlg(dlg) { LRect r(0, 0, PANE_X-1, PANE_Y-1); SetPos(r); Name(LLoadString(L_PROGRESSDLG_PROGRESS, "Progress")); SetId(IDC_PANE); Ref = 0; if (AddView(t = new LTableLayout(IDC_TABLE))) { OnPosChange(); #define PAD c->Padding(LCss::Len(LCss::LenPx, 1)); #define COLUMNS 3 int Row = 0, Col = 0; LLayoutCell *c = t->GetCell(0, Row++, true, COLUMNS, 1); PAD c->Height("1.2em"); // This stops the layout flickering c->Add(Desc = new LTextLabel(IDC_DESCRIPTION, 0, 0, -1, -1, "##")); c = t->GetCell(Col++, Row); PAD c->Add(ValText = new LTextLabel(IDC_VALUE, 0, 0, -1, -1, "##")); c = t->GetCell(Col++, Row); PAD c->Add(Rate = new LTextLabel(IDC_RATE, 0, 0, -1, -1, "##")); c = t->GetCell(Col++, Row++); PAD c->Add(Remaining = new LTextLabel(IDC_REMAINING, 0, 0, -1, -1, "##")); c = t->GetCell(0, Row++, true, COLUMNS, 1); PAD c->Add(Bar = new LProgressView(IDC_PROGRESS, 0, 0, PANE_X - 14, 10, "Progress")); c = t->GetCell(0, Row++, true, COLUMNS, 1); PAD c->TextAlign(LCss::Len(LCss::AlignCenter)); c->Add(But = new LButton(IDC_BUTTON, 0, 0, -1, -1, LLoadString(L_PROGRESSDLG_REQ_ABORT, "Request Abort"))); } } LProgressPane::~LProgressPane() { } bool LProgressPane::SetRange(const LRange &r) { UiDirty = true; Progress::SetRange(r); if (Bar) Bar->SetRange(r); if (InThread()) { LProgressDlg *Pd = dynamic_cast(GetParent()); if (Pd && But) But->Enabled(Pd->CanCancel); } return true; } void LProgressPane::SetStartTs() { Start = LCurrentTime(); StartDt.SetNow(); } void LProgressPane::UpdateUI() { if (!UiDirty) return; char Str[256]; bool Update = false; UiDirty = false; uint64 Now = LCurrentTime(); if (Start == 0) { // initialize the clock Start = Now; StartDt.SetNow(); } else if (Rate) { // calc rate double ElapsedSeconds = ((double)(Now - Start)) / 1000.0; double PerSec = 0.0; if (ElapsedSeconds != 0.0) PerSec = ((double) Val - Low) / ElapsedSeconds; sprintf_s(Str, sizeof(Str), LLoadString(L_PROGRESSDLG_RATE_FMT, "@ %.2f %s / sec"), PerSec * Scale, (Type) ? Type.Get() : ""); Update |= Rate->Name(Str); if (Remaining && PerSec > 0.0 && StartDt.IsValid()) { auto TotalSeconds = (High - Low + 1) / PerSec; // auto RemainingSeconds = TotalSeconds - ElapsedSeconds; LDateTime End; End.Set(StartDt.Ts() + (uint64_t)(TotalSeconds * LDateTime::Second64Bit)); if (auto Dur = LDateTime::Now().DescribePeriod(End)) Update |= Remaining->Name(Dur); } } if (ValText) { auto ValFmt = LLoadString(L_PROGRESSDLG_VALUE_FMT, "%g of %g %s"); sprintf_s(Str, sizeof(Str), ValFmt, (double)Val * Scale, (double)(High - Low) * Scale, (Type) ? Type.Get() : ""); Update |= ValText->Name(Str); } if (Bar) { if (High != Low) { #ifdef ALT_SCALE double Raw = ((double) v - Low) / ((double) High - Low); Bar->Value((int)(Raw * ALT_SCALE)); #else Bar->Value(Value()); #endif Bar->Invalidate(); } else { Bar->Value(0); } } if (ValText) ValText->SendNotify(LNotifyTableLayoutRefresh); } void LProgressPane::Value(int64 v) { Progress::Value(v); UiDirty = true; if (Dlg) Dlg->TimeCheck(); } void LProgressPane::OnCreate() { AttachChildren(); } LProgressPane &LProgressPane::operator++(int) { Value(Progress::Value() + 1); return *this; } LProgressPane &LProgressPane::operator--(int) { Value(Progress::Value() - 1); return *this; } int LProgressPane::OnNotify(LViewI *Ctrl, LNotification n) { switch (Ctrl->GetId()) { case IDC_TABLE: { if (n.Type == LNotifyTableLayoutChanged) { LRect p = GetPos(); LRect tbl_pos = t->GetPos(); LRect tbl_req = t->GetUsedArea(); if (tbl_req.Valid() && tbl_req.Y() > tbl_pos.Y()) { p.y2 = p.y1 + (tbl_req.Y() + LTableLayout::CellSpacing * 2); SetPos(p); SendNotify(LNotifyTableLayoutChanged); } } break; } case IDC_BUTTON: { Cancel(true); if (But) - { But->Name("Waiting..."); - } break; } } return 0; } void LProgressPane::OnPaint(LSurface *pDC) { LRect r = GetClient(); LThinBorder(pDC, r, DefaultRaisedEdge); pDC->Colour(L_MED); pDC->Rectangle(&r); } void LProgressPane::OnPosChange() { if (t) { LRect cr = GetClient(); cr.Inset(LTableLayout::CellSpacing, LTableLayout::CellSpacing); t->SetPos(cr); } } LFont *LProgressPane::GetFont() { // GdcBeTtf *Fnt = LSysFont; // return (Fnt) ? Fnt->Handle() : 0; return 0; } void LProgressPane::SetDescription(const char *d) { Progress::SetDescription(d); if (Desc) { Desc->Name(d); Desc->SendNotify(LNotifyTableLayoutRefresh); } } ///////////////////////////////////////////////////////////////////////////////////////// // #ifdef WIN32 // #define DefX ((GetSystemMetrics(SM_CXDLGFRAME) * 2)) // #define DefY ((GetSystemMetrics(SM_CYDLGFRAME) * 2) + GetSystemMetrics(SM_CYCAPTION)) // #else #define DefX LAppInst->GetMetric(LGI_MET_DECOR_X) #define DefY LAppInst->GetMetric(LGI_MET_DECOR_Y) // #endif LProgressDlg::LProgressDlg(LView *parent, uint64 timeout) { Ts = LCurrentTime(); Timeout = timeout; CanCancel = true; SetParent(parent); Resize(); if (parent) MoveSameScreen(parent); else MoveToCenter(); Name(LLoadString(L_PROGRESSDLG_PROGRESS, "Progress")); if (Timeout == 0) DoModeless(); else Push(); } LProgressDlg::~LProgressDlg() { if (Visible()) EndModeless(true); } bool LProgressDlg::OnRequestClose(bool OsClose) { for (auto p: Panes) p->Cancel(true); return false; } void LProgressDlg::Resize() { LRect r, c = GetPos(); int DecorX = LAppInst->GetMetric(LGI_MET_DECOR_X); int DecorY = LAppInst->GetMetric(LGI_MET_DECOR_Y); size_t Items = MAX(1, Panes.Length()); int Width = DecorX + PANE_X; int Height = (int) (DecorY + (PANE_Y * Items)); r.ZOff(Width - 1, Height - 1); r.Offset(c.x1, c.y1); SetPos(r); // Layout all the panes... int y = 0; for (auto p: Panes) { LRect r(0, y, PANE_X - 1, y + PANE_Y - 1); p->SetPos(r); p->Visible(true); y = r.y2 + 1; } } void LProgressDlg::OnCreate() { if (Panes.Length() == 0) Push(); SetPulse(500); } void LProgressDlg::OnPulse() { for (auto p: Panes) p->UpdateUI(); } void LProgressDlg::OnPosChange() { LRect c = GetClient(); // Layout all the panes... int y = 0; for (auto p: Panes) { LRect r = p->GetPos(); r.Offset(0, y-r.y1); r.x2 = c.x2; p->SetPos(r); p->Visible(true); y = r.y2 + 1; } } int LProgressDlg::OnNotify(LViewI *Ctrl, LNotification n) { if (Ctrl->GetId() == IDC_PANE && n.Type == LNotifyTableLayoutChanged) { // This code recalculates the size needed by all the progress panes // and then resizes the window to contain them all. LRect u(0, 0, -1, -1); for (auto p: Panes) { LRect r = p->GetPos(); if (u.Valid()) u.Union(&r); else u = r; } if (u.Valid()) { int x = u.X(); int y = u.Y(); LRect p = GetPos(); p.SetSize(x + LAppInst->GetMetric(LGI_MET_DECOR_X), y + LAppInst->GetMetric(LGI_MET_DECOR_Y)); SetPos(p); } } return 0; } LMessage::Result LProgressDlg::OnEvent(LMessage *Msg) { switch (Msg->Msg()) { #ifdef WIN32 case WM_CLOSE: { return 0; } #endif } return LDialog::OnEvent(Msg); } LProgressPane *LProgressDlg::ItemAt(int i) { return Panes.ItemAt(i); } LProgressPane *LProgressDlg::Push() { LProgressPane *Pane = new LProgressPane(this); if (Pane) { // Attach the new pane.. if (Visible()) Pane->Attach(this); else AddView(Pane); Panes.Add(Pane); Resize(); } return Pane; } void LProgressDlg::Pop(LProgressPane *p) { LProgressPane *Pane = (p) ? p : Panes.Last(); if (Pane) { Pane->Detach(); Panes.Delete(Pane); LView::Invalidate(); DeleteObj(Pane); Resize(); } } void LProgressDlg::SetCanCancel(bool cc) { CanCancel = cc; } LString LProgressDlg::GetDescription() { LString s; if (Panes.Length()) s = Panes.First()->GetDescription(); return s; } void LProgressDlg::SetDescription(const char *d) { if (Panes.Length()) Panes.First()->SetDescription(d); } LRange LProgressDlg::GetRange() { if (Panes.Length()) return Panes.First()->GetRange(); return LRange(); } bool LProgressDlg::SetRange(const LRange &r) { if (!Panes.Length()) return false; Panes.First()->SetRange(r); return true; } LProgressDlg &LProgressDlg::operator++(int) { if (Panes.Length()) { auto p = Panes.First(); (*p)++; } return *this; } LProgressDlg &LProgressDlg::operator--(int) { if (Panes.Length()) { auto p = Panes.First(); (*p)--; } return *this; } void LProgressDlg::TimeCheck() { if (!InThread()) return; uint64 Now = LCurrentTime(); if (Timeout) { if (Now - Ts >= Timeout) { DoModeless(); Timeout = 0; } } } int64 LProgressDlg::Value() { return Panes.Length() ? Panes.First()->Value() : -1; } void LProgressDlg::Value(int64 v) { if (Panes.Length()) Panes.First()->Value(v); } double LProgressDlg::GetScale() { return Panes.Length() ? Panes.First()->GetScale() : 0.0; } void LProgressDlg::SetScale(double s) { if (Panes.Length()) Panes.First()->SetScale(s); } LString LProgressDlg::GetType() { return Panes.Length() ? Panes.First()->GetType() : NULL; } void LProgressDlg::SetType(const char *t) { if (Panes.Length()) Panes.First()->SetType(t); } bool LProgressDlg::IsCancelled() { for (auto p: Panes) { if (p->IsCancelled()) return true; } return false; } void LProgressDlg::OnPaint(LSurface *pDC) { pDC->Colour(L_MED); pDC->Rectangle(); }