"Fossies" - the Fresh Open Source Software Archive

Member "filezilla-3.45.1/src/interface/filelistctrl.h" (25 Sep 2019, 17153 Bytes) of package /linux/misc/FileZilla_3.45.1_src.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "filelistctrl.h" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.45.0_vs_3.45.1.

    1 #ifndef FILEZILLA_INTERFACE_FILELISTCTRL_HEADER
    2 #define FILEZILLA_INTERFACE_FILELISTCTRL_HEADER
    3 
    4 #include "listctrlex.h"
    5 #include "systemimagelist.h"
    6 #include "listingcomparison.h"
    7 
    8 #include <cstring>
    9 #include <memory>
   10 
   11 class CQueueView;
   12 class CFileListCtrl_SortComparisonObject;
   13 class CFilelistStatusBar;
   14 #if defined(__WXGTK__) && !defined(__WXGTK3__)
   15 class CGtkEventCallbackProxyBase;
   16 #endif
   17 
   18 class CGenericFileData
   19 {
   20 public:
   21     std::wstring fileType;
   22     int icon{-2};
   23 
   24     // t_fileEntryFlags is defined in listingcomparison.h as it will be used for
   25     // both local and remote listings
   26     CComparableListing::t_fileEntryFlags comparison_flags{CComparableListing::normal};
   27 };
   28 
   29 class CFileListCtrlSortBase
   30 {
   31 public:
   32     CFileListCtrlSortBase() = default;
   33     CFileListCtrlSortBase(CFileListCtrlSortBase const&) = delete;
   34     CFileListCtrlSortBase& operator=(CFileListCtrlSortBase const&) = delete;
   35 
   36     enum DirSortMode
   37     {
   38         dirsort_ontop,
   39         dirsort_onbottom,
   40         dirsort_inline
   41     };
   42     enum NameSortMode
   43     {
   44         namesort_caseinsensitive,
   45         namesort_casesensitive,
   46         namesort_natural
   47     };
   48 
   49     virtual bool operator()(int a, int b) const = 0;
   50     virtual ~CFileListCtrlSortBase() {} // Without this empty destructor GCC complains
   51 
   52     #define CMP(f, data1, data2) \
   53         {\
   54             int res = this->f(data1, data2);\
   55             if (res < 0)\
   56                 return true;\
   57             else if (res > 0)\
   58                 return false;\
   59         }
   60 
   61     #define CMP_LESS(f, data1, data2) \
   62         {\
   63             int res = this->f(data1, data2);\
   64             if (res < 0)\
   65                 return true;\
   66             else\
   67                 return false;\
   68         }
   69 
   70     static int CmpCase(std::wstring_view const& str1, std::wstring_view const& str2)
   71     {
   72         return str1.compare(str2);
   73     }
   74 
   75     static int CmpNoCase(std::wstring_view const& str1, std::wstring_view const& str2)
   76     {
   77         int cmp = fz::stricmp(str1, str2);
   78         if (cmp) {
   79             return cmp;
   80         }
   81         return str1.compare(str2);
   82     }
   83 
   84     static int CmpNatural(std::wstring_view const& str1, std::wstring_view const& str2)
   85     {
   86         wchar_t const* p1 = str1.data();
   87         wchar_t const* p2 = str2.data();
   88 
   89         wchar_t const* const end1 = p1 + str1.size();
   90         wchar_t const* const end2 = p2 + str2.size();
   91 
   92         int res = 0;
   93         int zeroCount = 0;
   94         bool isNumber = false;
   95         for (; p1 != end1 && p2 != end2; ++p1, ++p2) {
   96             int diff = static_cast<int>(wxTolower(*p1)) - static_cast<int>(wxTolower(*p2));
   97             if (isNumber) {
   98                 if (res == 0) {
   99                     res = diff;
  100                 }
  101                 int nbDigits = (wxIsdigit(*p1) ? 1 : 0) + (wxIsdigit(*p2) ? 1 : 0);
  102                 if (nbDigits == 0 && res == 0) {
  103                     if (zeroCount) {
  104                         break;
  105                     }
  106                     isNumber = false;
  107                 }
  108                 else if (nbDigits != 2) {
  109                     break;
  110                 }
  111             }
  112             else if (wxIsdigit(*p1) && wxIsdigit(*p2)) {
  113                 zeroCount = 0;
  114                 for (; *p1 == '0' && (p1 + 1) != end1 && wxIsdigit(*(p1 + 1)); ++p1) {
  115                     zeroCount++;
  116                 }
  117                 for (; *p2 == '0' && (p2 + 1) != end2 && wxIsdigit(*(p2 + 1)); ++p2) {
  118                     zeroCount--;
  119                 }
  120                 res = *p1 - *p2;
  121                 isNumber = true;
  122             }
  123             else if (diff) {
  124                 return diff;
  125             }
  126         }
  127 
  128         if (res == 0 && isNumber) {
  129             res = zeroCount;
  130         }
  131 
  132         if (p1 == end1 && p2 == end2) {
  133             return res;
  134         }
  135         if (!isNumber || res == 0) {
  136             return (p1 == end1) ? -1 : 1;
  137         }
  138         if (p1 != end1 && wxIsdigit(*p1)) {
  139             return 1;       //more digits
  140         }
  141         if (p2 != end2 && wxIsdigit(*p2)) {
  142             return -1;      //fewer digits
  143         }
  144         return res;         //same length, compare first different digit in the sequence*/
  145     }
  146 
  147     typedef int (* CompareFunction)(std::wstring_view const&, std::wstring_view const&);
  148     static CompareFunction GetCmpFunction(NameSortMode mode)
  149     {
  150         switch (mode)
  151         {
  152         default:
  153         case CFileListCtrlSortBase::namesort_caseinsensitive:
  154             return &CFileListCtrlSortBase::CmpNoCase;
  155         case CFileListCtrlSortBase::namesort_casesensitive:
  156             return &CFileListCtrlSortBase::CmpCase;
  157         case CFileListCtrlSortBase::namesort_natural:
  158             return &CFileListCtrlSortBase::CmpNatural;
  159         }
  160     }
  161 };
  162 
  163 // Helper classes for fast sorting using std::sort
  164 // -----------------------------------------------
  165 
  166 template<typename value_type>
  167 inline int DoCmpName(value_type const& data1, value_type const& data2, CFileListCtrlSortBase::NameSortMode const nameSortMode)
  168 {
  169     switch (nameSortMode)
  170     {
  171     case CFileListCtrlSortBase::namesort_casesensitive:
  172         return CFileListCtrlSortBase::CmpCase(data1.name, data2.name);
  173 
  174     default:
  175     case CFileListCtrlSortBase::namesort_caseinsensitive:
  176         return CFileListCtrlSortBase::CmpNoCase(data1.name, data2.name);
  177 
  178     case CFileListCtrlSortBase::namesort_natural:
  179         return CFileListCtrlSortBase::CmpNatural(data1.name, data2.name);
  180     }
  181 }
  182 
  183 template<typename Listing>
  184 class CFileListCtrlSort : public CFileListCtrlSortBase
  185 {
  186 public:
  187     typedef Listing List;
  188     typedef typename Listing::value_type value_type;
  189 
  190     CFileListCtrlSort(Listing const& listing, DirSortMode dirSortMode, NameSortMode nameSortMode)
  191         : m_listing(listing), m_dirSortMode(dirSortMode), m_nameSortMode(nameSortMode)
  192     {
  193     }
  194 
  195     inline int CmpDir(value_type const& data1, value_type const& data2) const
  196     {
  197         switch (m_dirSortMode)
  198         {
  199         default:
  200         case dirsort_ontop:
  201             if (data1.is_dir()) {
  202                 if (!data2.is_dir()) {
  203                     return -1;
  204                 }
  205                 else {
  206                     return 0;
  207                 }
  208             }
  209             else {
  210                 if (data2.is_dir()) {
  211                     return 1;
  212                 }
  213                 else {
  214                     return 0;
  215                 }
  216             }
  217         case dirsort_onbottom:
  218             if (data1.is_dir()) {
  219                 if (!data2.is_dir()) {
  220                     return 1;
  221                 }
  222                 else {
  223                     return 0;
  224                 }
  225             }
  226             else {
  227                 if (data2.is_dir()) {
  228                     return -1;
  229                 }
  230                 else {
  231                     return 0;
  232                 }
  233             }
  234         case dirsort_inline:
  235             return 0;
  236         }
  237     }
  238 
  239     template<typename value_type>
  240     inline int CmpName(value_type const& data1, value_type const& data2) const
  241     {
  242         return DoCmpName(data1, data2, m_nameSortMode);
  243     }
  244 
  245     inline int CmpSize(const value_type &data1, const value_type &data2) const
  246     {
  247         int64_t const diff = data1.size - data2.size;
  248         if (diff < 0) {
  249             return -1;
  250         }
  251         else if (diff > 0) {
  252             return 1;
  253         }
  254         else {
  255             return 0;
  256         }
  257     }
  258 
  259     inline int CmpStringNoCase(const wxString &data1, const wxString &data2) const
  260     {
  261         return data1.CmpNoCase(data2);
  262     }
  263 
  264     inline int CmpTime(const value_type &data1, const value_type &data2) const
  265     {
  266         if (data1.time < data2.time) {
  267             return -1;
  268         }
  269         else if (data1.time > data2.time) {
  270             return 1;
  271         }
  272         else {
  273             return 0;
  274         }
  275     }
  276 
  277 protected:
  278     Listing const& m_listing;
  279 
  280     DirSortMode const m_dirSortMode;
  281     NameSortMode const m_nameSortMode;
  282 };
  283 
  284 template<class CFileData> class CFileListCtrl;
  285 
  286 template<class T, typename DataEntry> class CReverseSort final : public T
  287 {
  288 public:
  289     CReverseSort(typename T::List const& listing, std::vector<DataEntry>& fileData, CFileListCtrlSortBase::DirSortMode dirSortMode, CFileListCtrlSortBase::NameSortMode nameSortMode, CFileListCtrl<DataEntry>* const pListView)
  290         : T(listing, fileData, dirSortMode, nameSortMode, pListView)
  291     {
  292     }
  293 
  294     inline bool operator()(int a, int b) const
  295     {
  296         return T::operator()(b, a);
  297     }
  298 };
  299 
  300 template<typename Listing, typename DataEntry>
  301 class CFileListCtrlSortName : public CFileListCtrlSort<Listing>
  302 {
  303 public:
  304     CFileListCtrlSortName(Listing const& listing, std::vector<DataEntry>&, CFileListCtrlSortBase::DirSortMode dirSortMode, CFileListCtrlSortBase::NameSortMode nameSortMode, CFileListCtrl<DataEntry>* const)
  305         : CFileListCtrlSort<Listing>(listing, dirSortMode, nameSortMode)
  306     {
  307     }
  308 
  309     bool operator()(int a, int b) const
  310     {
  311         typename Listing::value_type const& data1 = this->m_listing[a];
  312         typename Listing::value_type const& data2 = this->m_listing[b];
  313 
  314         CMP(CmpDir, data1, data2);
  315 
  316         CMP_LESS(CmpName, data1, data2);
  317     }
  318 };
  319 
  320 template<typename Listing, typename DataEntry>
  321 class CFileListCtrlSortSize : public CFileListCtrlSort<Listing>
  322 {
  323 public:
  324     CFileListCtrlSortSize(Listing const& listing, std::vector<DataEntry>&, CFileListCtrlSortBase::DirSortMode dirSortMode, CFileListCtrlSortBase::NameSortMode nameSortMode, CFileListCtrl<DataEntry>* const)
  325         : CFileListCtrlSort<Listing>(listing, dirSortMode, nameSortMode)
  326     {
  327     }
  328 
  329     bool operator()(int a, int b) const
  330     {
  331         typename Listing::value_type const& data1 = this->m_listing[a];
  332         typename Listing::value_type const& data2 = this->m_listing[b];
  333 
  334         CMP(CmpDir, data1, data2);
  335 
  336         CMP(CmpSize, data1, data2);
  337 
  338         CMP_LESS(CmpName, data1, data2);
  339     }
  340 };
  341 
  342 template<typename Listing, typename DataEntry>
  343 class CFileListCtrlSortType : public CFileListCtrlSort<Listing>
  344 {
  345 public:
  346     CFileListCtrlSortType(Listing const& listing, std::vector<DataEntry>& fileData, CFileListCtrlSortBase::DirSortMode dirSortMode, CFileListCtrlSortBase::NameSortMode nameSortMode, CFileListCtrl<DataEntry>* const pListView)
  347         : CFileListCtrlSort<Listing>(listing, dirSortMode, nameSortMode), m_pListView(pListView), m_fileData(fileData)
  348     {
  349     }
  350 
  351     bool operator()(int a, int b) const
  352     {
  353         typename Listing::value_type const& data1 = this->m_listing[a];
  354         typename Listing::value_type const& data2 = this->m_listing[b];
  355 
  356         CMP(CmpDir, data1, data2);
  357 
  358         DataEntry &type1 = m_fileData[a];
  359         DataEntry &type2 = m_fileData[b];
  360         if (type1.fileType.empty()) {
  361             type1.fileType = m_pListView->GetType(data1.name, data1.is_dir());
  362         }
  363         if (type2.fileType.empty()) {
  364             type2.fileType = m_pListView->GetType(data2.name, data2.is_dir());
  365         }
  366 
  367         CMP(CmpStringNoCase, type1.fileType, type2.fileType);
  368 
  369         CMP_LESS(CmpName, data1, data2);
  370     }
  371 
  372 protected:
  373     CFileListCtrl<DataEntry>* const m_pListView;
  374     std::vector<DataEntry>& m_fileData;
  375 };
  376 
  377 template<typename Listing, typename DataEntry>
  378 class CFileListCtrlSortTime : public CFileListCtrlSort<Listing>
  379 {
  380 public:
  381     CFileListCtrlSortTime(Listing const& listing, std::vector<DataEntry>&, CFileListCtrlSortBase::DirSortMode dirSortMode, CFileListCtrlSortBase::NameSortMode nameSortMode, CFileListCtrl<DataEntry>* const)
  382         : CFileListCtrlSort<Listing>(listing, dirSortMode, nameSortMode)
  383     {
  384     }
  385 
  386     bool operator()(int a, int b) const
  387     {
  388         typename Listing::value_type const& data1 = this->m_listing[a];
  389         typename Listing::value_type const& data2 = this->m_listing[b];
  390 
  391         CMP(CmpDir, data1, data2);
  392 
  393         CMP(CmpTime, data1, data2);
  394 
  395         CMP_LESS(CmpName, data1, data2);
  396     }
  397 };
  398 
  399 template<typename Listing, typename DataEntry>
  400 class CFileListCtrlSortPermissions : public CFileListCtrlSort<Listing>
  401 {
  402 public:
  403     CFileListCtrlSortPermissions(Listing const& listing, std::vector<DataEntry>&, CFileListCtrlSortBase::DirSortMode dirSortMode, CFileListCtrlSortBase::NameSortMode nameSortMode, CFileListCtrl<DataEntry>* const)
  404         : CFileListCtrlSort<Listing>(listing, dirSortMode, nameSortMode)
  405     {
  406     }
  407 
  408     bool operator()(int a, int b) const
  409     {
  410         typename Listing::value_type const& data1 = this->m_listing[a];
  411         typename Listing::value_type const& data2 = this->m_listing[b];
  412 
  413         CMP(CmpDir, data1, data2);
  414 
  415         CMP(CmpStringNoCase, *data1.permissions, *data2.permissions);
  416 
  417         CMP_LESS(CmpName, data1, data2);
  418     }
  419 };
  420 
  421 template<typename Listing, typename DataEntry>
  422 class CFileListCtrlSortOwnerGroup : public CFileListCtrlSort<Listing>
  423 {
  424 public:
  425     CFileListCtrlSortOwnerGroup(Listing const& listing, std::vector<DataEntry>&, CFileListCtrlSortBase::DirSortMode dirSortMode, CFileListCtrlSortBase::NameSortMode nameSortMode, CFileListCtrl<DataEntry>* const)
  426         : CFileListCtrlSort<Listing>(listing, dirSortMode, nameSortMode)
  427     {
  428     }
  429 
  430     bool operator()(int a, int b) const
  431     {
  432         typename Listing::value_type const& data1 = this->m_listing[a];
  433         typename Listing::value_type const& data2 = this->m_listing[b];
  434 
  435         CMP(CmpDir, data1, data2);
  436 
  437         CMP(CmpStringNoCase, *data1.ownerGroup, *data2.ownerGroup);
  438 
  439         CMP_LESS(CmpName, data1, data2);
  440     }
  441 };
  442 
  443 template<typename Listing, typename DataEntry>
  444 class CFileListCtrlSortPath : public CFileListCtrlSort<Listing>
  445 {
  446 public:
  447     CFileListCtrlSortPath(Listing const& listing, std::vector<DataEntry>& fileData, CFileListCtrlSortBase::DirSortMode dirSortMode, CFileListCtrlSortBase::NameSortMode nameSortMode, CFileListCtrl<DataEntry>* const)
  448         : CFileListCtrlSort<Listing>(listing, dirSortMode, nameSortMode)
  449         , m_fileData(fileData)
  450     {
  451     }
  452 
  453     bool operator()(int a, int b) const
  454     {
  455         typename Listing::value_type const& data1 = this->m_listing[a];
  456         typename Listing::value_type const& data2 = this->m_listing[b];
  457 
  458         if (data1.path < data2.path) {
  459             return true;
  460         }
  461         if (data1.path != data2.path) {
  462             return false;
  463         }
  464 
  465         CMP_LESS(CmpName, data1, data2);
  466     }
  467     std::vector<DataEntry>& m_fileData;
  468 };
  469 
  470 template<typename Listing, typename DataEntry>
  471 class CFileListCtrlSortNamePath : public CFileListCtrlSort<Listing>
  472 {
  473 public:
  474     CFileListCtrlSortNamePath(Listing const& listing, std::vector<DataEntry>& fileData, CFileListCtrlSortBase::DirSortMode dirSortMode, CFileListCtrlSortBase::NameSortMode nameSortMode, CFileListCtrl<DataEntry>* const)
  475         : CFileListCtrlSort<Listing>(listing, dirSortMode, nameSortMode)
  476         , m_fileData(fileData)
  477     {
  478     }
  479 
  480     bool operator()(int a, int b) const
  481     {
  482         typename Listing::value_type const& data1 = this->m_listing[a];
  483         typename Listing::value_type const& data2 = this->m_listing[b];
  484 
  485         CMP(CmpDir, data1, data2);
  486         CMP(CmpName, data1, data2);
  487 
  488         if (data1.path < data2.path) {
  489             return true;
  490         }
  491         if (data1.path != data2.path) {
  492             return false;
  493         }
  494 
  495         CMP_LESS(CmpName, data1, data2);
  496     }
  497     std::vector<DataEntry>& m_fileData;
  498 };
  499 
  500 namespace genericTypes {
  501     enum type {
  502         file,
  503         directory
  504     };
  505 }
  506 
  507 template<class CFileData> class CFileListCtrl : public wxListCtrlEx, public CComparableListing
  508 {
  509     template<typename Listing, typename DataEntry> friend class CFileListCtrlSortType;
  510 public:
  511     CFileListCtrl(wxWindow* pParent, CQueueView *pQueue, bool border = false);
  512     virtual ~CFileListCtrl() = default;
  513 
  514     void SetFilelistStatusBar(CFilelistStatusBar* pFilelistStatusBar) { m_pFilelistStatusBar = pFilelistStatusBar; }
  515     CFilelistStatusBar* GetFilelistStatusBar() { return m_pFilelistStatusBar; }
  516 
  517     void ClearSelection();
  518 
  519     virtual void OnNavigationEvent(bool) {}
  520 
  521     std::vector<unsigned int> const& indexMapping() const { return m_indexMapping; }
  522 
  523 protected:
  524     CQueueView *m_pQueue{};
  525 
  526     std::vector<CFileData> m_fileData;
  527     std::vector<unsigned int> m_indexMapping;
  528     std::vector<unsigned int> m_originalIndexMapping; // m_originalIndexMapping will only be set on comparisons
  529 
  530     virtual bool ItemIsDir(int index) const = 0;
  531     virtual int64_t ItemGetSize(int index) const = 0;
  532 
  533     std::map<wxString, std::wstring> m_fileTypeMap;
  534 
  535     // The .. item
  536     bool m_hasParent{true};
  537 
  538     int m_sortColumn{-1};
  539     int m_sortDirection{};
  540 
  541     void InitSort(int optionID); // Has to be called after initializing columns
  542     void SortList(int column = -1, int direction = -1, bool updateSelections = true);
  543     CFileListCtrlSortBase::DirSortMode GetDirSortMode();
  544     CFileListCtrlSortBase::NameSortMode GetNameSortMode();
  545     virtual std::unique_ptr<CFileListCtrlSortBase> GetSortComparisonObject() = 0;
  546 
  547     // An empty path denotes a virtual file
  548     std::wstring GetType(std::wstring name, bool dir, std::wstring const& path = std::wstring());
  549 
  550     // Comparison related
  551     virtual void ScrollTopItem(int item);
  552     virtual void OnPostScroll();
  553     virtual void OnExitComparisonMode();
  554     virtual void CompareAddFile(t_fileEntryFlags flags);
  555 
  556     int m_comparisonIndex{-1};
  557 
  558     // Remembers which non-fill items are selected if enabling/disabling comparison.
  559     // Exploit fact that sort order doesn't change -> O(n)
  560     void ComparisonRememberSelections();
  561     void ComparisonRestoreSelections();
  562     std::deque<int> m_comparisonSelections;
  563 
  564     CFilelistStatusBar* m_pFilelistStatusBar{};
  565 
  566     // Indexes of the items added, sorted ascending.
  567     void UpdateSelections_ItemsAdded(std::vector<int> const& added_indexes);
  568 
  569 #ifndef __WXMSW__
  570     // Generic wxListCtrl does not support wxLIST_STATE_DROPHILITED, emulate it
  571     wxListItemAttr m_dropHighlightAttribute;
  572 #endif
  573 
  574     void SetSelection(int item, bool select);
  575 #ifndef __WXMSW__
  576     // Used by selection tracking
  577     void SetItemCount(int count);
  578 #endif
  579 
  580 #ifdef __WXMSW__
  581     virtual int GetOverlayIndex(int) { return 0; }
  582 #endif
  583 
  584 private:
  585     void UpdateSelections(int min, int max);
  586 
  587     void SortList_UpdateSelections(bool* selections, int focused_item, unsigned int focused_index);
  588 
  589     // If this is set to true, don't process selection changed events
  590     bool m_insideSetSelection{};
  591 
  592 #ifdef __WXMSW__
  593     virtual WXLRESULT MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam);
  594     virtual bool MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result);
  595 #else
  596     int m_focusItem{-1};
  597     std::vector<bool> m_selections;
  598     int m_pending_focus_processing{};
  599 #endif
  600 
  601 #if defined(__WXGTK__) && !defined(__WXGTK3__)
  602     std::unique_ptr<CGtkEventCallbackProxyBase> m_gtkEventCallbackProxy;
  603 #endif
  604 
  605     std::wstring m_genericTypes[2];
  606 
  607     DECLARE_EVENT_TABLE()
  608     void OnColumnClicked(wxListEvent &event);
  609     void OnColumnRightClicked(wxListEvent& event);
  610     void OnItemSelected(wxListEvent& event);
  611     void OnItemDeselected(wxListEvent& event);
  612 #ifndef __WXMSW__
  613     void OnFocusChanged(wxListEvent& event);
  614     void OnProcessFocusChange(wxCommandEvent& event);
  615     void OnLeftDown(wxMouseEvent& event);
  616     void OnProcessMouseEvent(wxCommandEvent& event);
  617 #endif
  618     void OnKeyDown(wxKeyEvent& event);
  619 };
  620 
  621 class SortPredicate
  622 {
  623 public:
  624     SortPredicate() = delete;
  625     SortPredicate(std::unique_ptr<CFileListCtrlSortBase> const& ref)
  626         : p_(ref.get())
  627     {}
  628 
  629     inline bool operator()(int lhs, int rhs) {
  630         return (*p_)(lhs, rhs);
  631     }
  632 
  633 public:
  634     CFileListCtrlSortBase const* p_;
  635 };
  636 
  637 #ifdef FILELISTCTRL_INCLUDE_TEMPLATE_DEFINITION
  638 #include "filelistctrl.cpp"
  639 #endif
  640 
  641 #endif