File indexing completed on 2024-04-14 15:52:55

0001 /*
0002     This file is part of the Okteta Gui library, made within the KDE community.
0003 
0004     SPDX-FileCopyrightText: 2003, 2008-2009 Friedrich W. H. Kossebau <kossebau@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0007 */
0008 
0009 #ifndef OKTETA_SELECTION_HPP
0010 #define OKTETA_SELECTION_HPP
0011 
0012 // Okteta core
0013 #include <Okteta/AddressRange>
0014 
0015 namespace Okteta {
0016 
0017 /** This class describes a selected range of the buffer.
0018  * As it is used as selection controlled by
0019  * mouse and keyboard commands it offers two ways to set its range:
0020  * - by giving the startposition (of the cursor) of an interactive selection
0021  *   and the subsequent end positions (until selection is finished)
0022  * - direct setting (as provided by AddressRange)
0023  *
0024  * the interactive selection takes care that
0025  *
0026  * @author Friedrich W. H.  Kossebau
0027  */
0028 class Selection
0029 {
0030 public:
0031     /** creates a selection with a given start.
0032      * @param index index in front of which the selection begins
0033      */
0034     explicit Selection(Address index);
0035     Selection(const Selection& other);
0036     /** creates an invalid selection */
0037     Selection();
0038 
0039     ~Selection();
0040 
0041 public:
0042     Selection& operator=(const Selection& other);
0043     Selection& operator=(const AddressRange& range);
0044 
0045     bool operator==(const Selection& other) const;
0046     bool operator!=(const Selection& other) const;
0047 
0048 public: // modification access
0049     /** starts the selection.
0050      * For this the anchor, start and end are set to the given index,
0051      * so the initial selection is empty.
0052      * @param index index in front of which the selection begins
0053      */
0054     void setStart(Address index);
0055     /** sets the end of the current selection.
0056      * If the end is before the start the selection will reach from the given index
0057      * @param index index in front of which the selection ends
0058      */
0059     void setEnd(Address index);
0060     /** sets the selection to be invalid
0061      */
0062     void cancel();
0063     /** sets the anchor to the start or the end.
0064      * @param forward true to the start, otherwise to the end
0065      * If the selection has not started the behaviour is undefined.
0066      */
0067     void setForward(bool forward = true);
0068     /** swaps anchor from start to end or vice versa.
0069      * If the selection has not started the behaviour is undefined.
0070      */
0071     void reverse();
0072 
0073     void adaptToReplacement(Address pos, Size removedLength, Size insertedLength);
0074     void adaptToSwap(Address firstOffset, Address secondOffset, Size secondLength);
0075 
0076 public: // value access
0077     /**
0078      * @return anchor value
0079      */
0080     Address anchor() const;
0081     Address start() const;
0082     Address end() const;
0083     Address nextBeforeStart() const;
0084     Address nextBehindEnd() const;
0085     /**
0086      * @return range
0087      */
0088     const AddressRange& range() const;
0089 
0090 public: // logic access
0091     bool isValid() const;
0092     /**
0093      * @return @c true if the anchor has been set, otherwise @c false.
0094      */
0095     bool started() const;
0096     /**
0097      * @return @c true if the anchor has been set and the selection is empty, otherwise @c false.
0098      */
0099     bool justStarted() const;
0100     /**
0101      * @return @c true if the anchor is at the begin of the selection
0102      */
0103     bool isForward() const;
0104 
0105 private:
0106     /** mRange */
0107     AddressRange mRange;
0108     /** cursor index where the selection starts */
0109     Address mAnchor = -1;
0110 };
0111 
0112 inline Selection::Selection() = default;
0113 inline Selection::Selection(const Selection& other) = default;
0114 inline Selection::Selection(Address index) : mAnchor(index)  {}
0115 inline Selection::~Selection() = default;
0116 
0117 inline Selection& Selection::operator=(const Selection& other) = default;
0118 
0119 inline Selection& Selection::operator=(const AddressRange& range)
0120 {
0121     mRange = range;
0122     mAnchor = range.start();
0123     return *this;
0124 }
0125 
0126 inline bool Selection::operator==(const Selection& other) const
0127 {
0128     return (mRange == other.mRange) && (mAnchor == other.mAnchor);
0129 }
0130 inline bool Selection::operator!=(const Selection& other) const
0131 {
0132     return (mRange != other.mRange) || (mAnchor != other.mAnchor);
0133 }
0134 
0135 inline void Selection::setStart(Address index)
0136 {
0137     mAnchor = index;
0138     mRange.unset();
0139 }
0140 
0141 inline void Selection::setEnd(Address index)
0142 {
0143     // nothing selected?
0144     if (index == mAnchor) {
0145         mRange.unset();
0146     }
0147     // selecting forwards?
0148     else if (index > mAnchor) {
0149         mRange.setStart(mAnchor);
0150         mRange.setEnd(index - 1);
0151     }
0152     // selecting backwards
0153     else {
0154         mRange.setStart(index);
0155         mRange.setEnd(mAnchor - 1);
0156     }
0157 }
0158 
0159 inline void Selection::reverse()
0160 {
0161     mAnchor = isForward() ? mRange.nextBehindEnd() : mRange.start();
0162 }
0163 
0164 inline void Selection::setForward(bool Forward)
0165 {
0166     mAnchor = Forward ? mRange.start() : mRange.nextBehindEnd();
0167 }
0168 
0169 inline const AddressRange& Selection::range()   const { return mRange; }
0170 inline Address Selection::anchor()              const { return mAnchor; }
0171 inline Address Selection::start()               const { return mRange.start(); }
0172 inline Address Selection::end()                 const { return mRange.end(); }
0173 inline Address Selection::nextBeforeStart()     const { return mRange.nextBeforeStart(); }
0174 inline Address Selection::nextBehindEnd()       const { return mRange.nextBehindEnd(); }
0175 
0176 inline void Selection::cancel() { mAnchor = -1; mRange.unset(); }
0177 
0178 inline bool Selection::isValid()     const { return mRange.isValid(); }
0179 inline bool Selection::started()     const { return mAnchor != -1; }
0180 inline bool Selection::justStarted() const { return mAnchor != -1 && mRange.start() == -1; }
0181 inline bool Selection::isForward()   const { return mAnchor == mRange.start(); }
0182 
0183 inline void Selection::adaptToReplacement(Address pos, Size removedLength, Size insertedLength)
0184 {
0185     mRange.adaptToReplacement(pos, removedLength, insertedLength);
0186     mAnchor = isForward() ? mRange.start() : mRange.nextBehindEnd();
0187 }
0188 
0189 inline void Selection::adaptToSwap(Address firstOffset, Address secondOffset, Size secondLength)
0190 {
0191     // no intersection?
0192     if (mRange.end() < firstOffset || mRange.start() > secondOffset + secondLength - 1) {
0193         return;
0194     }
0195 
0196     const AddressRange firstSection(firstOffset, secondOffset - 1);
0197     if (firstSection.includes(mRange)) {
0198         mRange.moveBy(secondLength);
0199     } else {
0200         const AddressRange secondRange = AddressRange::fromWidth(secondOffset, secondLength);
0201         if (secondRange.includes(mRange)) {
0202             mRange.moveBy(-firstSection.width());
0203         } else {
0204             mRange.unset();
0205         }
0206     }
0207 }
0208 
0209 }
0210 
0211 #endif