Mercurial > dillo_port1.3
view dw/selection.hh @ 1733:f39cfc38ea10
rework nonCssHints API of StyleEngine
* Instead of passing the nonCssHints as a CssPropertyList, set the hints
separately and create the list in StyleEngine.
* The CssPropertyList holding the nonCssHints is now completely managed
by StyleEngine and kept on the stack.
* Replace the table_cell_props mechanic in html.cc/table.cc with a
new method inheritNonCssHints() in StyleEngine.
author | Johannes Hofmann <Johannes.Hofmann@gmx.de> |
---|---|
date | Mon, 11 Oct 2010 21:35:53 +0200 |
parents | f1a4a6efe1f1 |
children | 133dedffca47 |
line wrap: on
line source
#ifndef __DW_SELECTION_H__ #define __DW_SELECTION_H__ #ifndef __INCLUDED_FROM_DW_CORE_HH__ # error Do not include this file directly, use "core.hh" instead. #endif namespace dw { namespace core { /** * \brief This class handles selections, as well as activation of links, * which is closely related. * * <h3>General Overview</h3> * * dw::core::SelectionState is associated with dw::core::Layout. The selection * state is controlled by "abstract events", which are sent by single * widgets by calling one of the following methods: * * <ul> * <li> dw::core::SelectionState::buttonPress for button press events, * <li> dw::core::SelectionState::buttonRelease for button release events, and * <li> dw::core::SelectionState::buttonMotion for motion events (with pressed * mouse button). * </ul> * * The widget must construct simple iterators (dw::core::Iterator), which will * be transferred to deep iterators (dw::core::DeepIterator), see below for * more details. All event handling methods have the same signature, the * arguments in detail are: * * <table> * <tr><td>dw::core::Iterator *it <td>the iterator pointing on the item * under the mouse pointer; this * iterator \em must be created with * dw::core::Content::SELECTION_CONTENT * as mask * <tr><td>int charPos <td>the exact (character) position * within the iterator, * <tr><td>int linkNo <td>if this item is associated with a * link, its number (see * dw::core::Layout::LinkReceiver), * otherwise -1 * <tr><td>dw::core::EventButton *event <td>the event itself; only the button * is used * <tr><td>bool withinContent <td>true, if there is some selectable * content unter the mouse cursor; if * set to false, the "full screen" * feature is used on double click. * </table> * * Look also at dw::core::SelectionState::handleEvent, which may be useful * in some circumstances. * * In some cases, \em charPos would be difficult to determine. E.g., when * the dw::Textblock widget decides that the user is pointing on a position * <i>at the end</i> of an image (DwImage), it constructs a simple iterator * pointing on this image widget. In a simple iterator, that fact that * the pointer is at the end, would be represented by \em charPos == 1. But * when transferring this simple iterator into an deep iterator, this * simple iterator is discarded and instead the stack has an iterator * pointing to text at the top. As a result, only the first letter of the * ALT text would be copied. * * To avoid this problem, widgets should in this case pass * dw::core::SelectionState::END_OF_WORD as \em charPos, which is then * automatically reduced to the actual length of the deep(!) iterator. * * The return value is the same as in DwWidget event handling methods. * I.e., in most cases, they should simply return it. The events * dw::core::Layout::LinkReceiver::press, * dw::core::Layout::LinkReceiver::release and * dw::core::Layout::LinkReceiver::click (but not * dw::core::Layout::LinkReceiver::enter) are emitted by these methods, so * that widgets which let dw::core::SelectionState handle links, should only * emit dw::core::Layout::LinkReceiver::enter for themselves. * * <h3>Selection State</h3> * * Selection interferes with handling the activation of links, so the * latter is also handled by the dw::core::SelectionState. Details are based on * following guidelines: * * <ol> * <li> It should be simple to select links and to start selection in * links. The rule to distinguish between link activation and * selection is that the selection starts as soon as the user leaves * the link. (This is, IMO, a useful feature. Even after drag and * drop has been implemented in dillo, this should be somehow * preserved.) * * <li> The selection should stay as long as possible, i.e., the old * selection is only cleared when a new selection is started. * </ol> * * The latter leads to a model with two states: the selection state and * the link handling state. * * The general selection works, for events not pointing on links, like * this (numbers in parantheses after the event denote the button, "n" * means arbitrary button): * * \dot * digraph G { * node [shape=ellipse, fontname=Helvetica, fontsize=10]; * edge [arrowhead="open", labelfontname=Helvetica, labelfontsize=10, * color="#404040", labelfontcolor="#000080", * fontname=Helvetica, fontsize=10, fontcolor="#000080"]; * fontname=Helvetica; fontsize=10; * * NONE; * SELECTING; * q [label="Anything selected?", shape=plaintext]; * SELECTED; * * NONE -> SELECTING [label="press(1)\non non-link"]; * SELECTING -> SELECTING [label="motion(1)"]; * SELECTING -> q [label="release(1)"]; * q -> SELECTED [label="yes"]; * q -> NONE [label="no"]; * SELECTED -> SELECTING [label="press(1)"]; * * } * \enddot * * The selected region is represented by two instances of * dw::core::DeepIterator. * * Links are handled by a different state machine: * * \dot * digraph G { * node [shape=ellipse, fontname=Helvetica, fontsize=10]; * edge [arrowhead="open", labelfontname=Helvetica, labelfontsize=10, * color="#404040", labelfontcolor="#000080", * fontname=Helvetica, fontsize=10, fontcolor="#000080"]; * fontname=Helvetica; fontsize=10; * * LINK_NONE; * LINK_PRESSED; * click [label="Emit \"click\" signal.", shape=record]; * q11 [label="Still the same link?", shape=plaintext]; * q21 [label="Still the same link?", shape=plaintext]; * q22 [label="n == 1?", shape=plaintext]; * SELECTED [label="Switch selection\nto SELECTED", shape=record]; * q12 [label="n == 1?", shape=plaintext]; * SELECTING [label="Switch selection\nto SELECTING", shape=record]; * * LINK_NONE -> LINK_PRESSED [label="press(n)\non link"]; * LINK_PRESSED -> q11 [label="motion(n)"]; * q11 -> LINK_PRESSED [label="yes"]; * q11 -> q12 [label="no"]; * q12 -> SELECTING [label="yes"]; * SELECTING -> LINK_NONE; * q12 -> LINK_NONE [label="no"]; * LINK_PRESSED -> q21 [label="release(n)"]; * q21 -> click [label="yes"]; * click -> LINK_NONE; * q21 -> q22 [label="no"]; * q22 -> SELECTED [label="yes"]; * SELECTED -> LINK_NONE; * q22 -> LINK_NONE [label="no"]; * } * \enddot * * Switching selection simply means that the selection state will * eventually be SELECTED/SELECTING, with the original and the current * position making up the selection region. This happens for button 1, * events with buttons other than 1 do not affect selection at all. * * * \todo dw::core::SelectionState::buttonMotion currently always assumes * that button 1 has been pressed (since otherwise it would not do * anything). This should be made a bit cleaner. * * \todo The selection should be cleared, when the user selects something * somewhere else (perhaps switched into "non-active" mode, as e.g. Gtk+ * does). * */ class SelectionState { public: enum { END_OF_WORD = 1 << 30 }; class DoubleClickReceiver: public lout::signal::Receiver { public: virtual void doubleClick () = 0; }; private: class DoubleClickEmitter: public lout::signal::Emitter { private: enum { DOUBLE_CLICK }; protected: bool emitToReceiver (lout::signal::Receiver *receiver, int signalNo, int argc, Object **argv); public: inline void connectDoubleClick (DoubleClickReceiver *receiver) { connect (receiver); } inline void emitDoubleClick () { emitVoid (DOUBLE_CLICK, 0, NULL); } }; DoubleClickEmitter doubleClickEmitter; Layout *layout; // selection enum { NONE, SELECTING, SELECTED } selectionState; DeepIterator *from, *to; int fromChar, toChar; // link handling enum { LINK_NONE, LINK_PRESSED } linkState; int linkButton; DeepIterator *link; int linkChar, linkNumber; void resetSelection (); void resetLink (); void switchLinkToSelection (Iterator *it, int charPos); void adjustSelection (Iterator *it, int charPos); static int correctCharPos (DeepIterator *it, int charPos); void highlight (bool fl, int dir) { highlight0 (fl, from, fromChar, to, toChar, dir); } void highlight0 (bool fl, DeepIterator *from, int fromChar, DeepIterator *to, int toChar, int dir); void copy (); public: enum EventType { BUTTON_PRESS, BUTTON_RELEASE, BUTTON_MOTION }; SelectionState (); ~SelectionState (); inline void setLayout (Layout *layout) { this->layout = layout; } void reset (); inline void connectDoubleClick (DoubleClickReceiver *receiver) { doubleClickEmitter.connectDoubleClick (receiver); } bool buttonPress (Iterator *it, int charPos, int linkNo, EventButton *event, bool withinContent); bool buttonRelease (Iterator *it, int charPos, int linkNo, EventButton *event, bool withinContent); bool buttonMotion (Iterator *it, int charPos, int linkNo, EventMotion *event, bool withinContent); bool handleEvent (EventType eventType, Iterator *it, int charPos, int linkNo, MousePositionEvent *event, bool withinContent); }; } // namespace dw } // namespace core #endif // __DW_SELECTION_H__