[go: up one dir, main page]

Menu

[80c337]: / qtextdocumentutils.h  Maximize  Restore  History

Download this file

1029 lines (987 with data), 47.3 kB

   1
   2
   3
   4
   5
   6
   7
   8
   9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  51
  52
  53
  54
  55
  56
  57
  58
  59
  60
  61
  62
  63
  64
  65
  66
  67
  68
  69
  70
  71
  72
  73
  74
  75
  76
  77
  78
  79
  80
  81
  82
  83
  84
  85
  86
  87
  88
  89
  90
  91
  92
  93
  94
  95
  96
  97
  98
  99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
#ifndef QTEXTDOCUMENTUTILS_H
#define QTEXTDOCUMENTUTILS_H
/** \file qtextdocumentutils.h
*
* \brief A collection of utilities for dealing with QTextDocument structures
*
* The data and routines in this file serve a few purposes.
*
* First, it imports qtextdocumentlobs.h as its foundation.
* (See documentation on that file for more information on what it defines.)
*
* Second, QTextDocument and related classes are an excellent set of document utilities, but do
* not provide every last feature I would like for the Lurch word processor. Thus this file
* provides a collection of tools works with QTextBlocks and other document elements in ways that
* fill in the gaps.
*
* Third, it creates the QTextNode class for interacting with QTextDocuments using a hierarchical
* API rather than a QTextCursor-based API.
*/
#include <QTextBlock>
#include "qtextdocumentlobs.h"
/** \brief Ordered list of text list styles
*
* Bulleted list styles are given first, followed by numbered list styles.
* They are roughly from "outer levels of an outline" to "inner levels of an outline"
* order within those two categories.
*
* This is a comprehensive list of all text list styles.
*/
const QList<QTextListFormat::Style> getTextListStyles ();
/** \brief Given a list style, return its name in a human-readable format
*
* E.g., results like "Bullet" or "A." are returned.
*/
QString listFormatToName ( QTextListFormat::Style s );
/** \brief Given a style name, return its style datum
*
* This function is the inverse of listFormatToName().
*/
QTextListFormat::Style nameToListFormat ( QString name );
/** \brief Given a list style, this returns the next list style
*
* "Next" in this case is determined by the order in which the styles appear in
* textListStyles. The order wraps around in a circle, so "next" after the last is the first.
*/
QTextListFormat::Style nextListStyle ( QTextListFormat::Style s );
/** \brief Given a list style, this returns the previous list style
*
* This function is the inverse of nextListFormat(), including the wrapping around the list.
*/
QTextListFormat::Style previousListStyle ( QTextListFormat::Style s );
/** \brief Returns whether the given text block is the first block in a QTextList
*
* If \a b is not in a list, the result is false.
* If \a b is in a list but there are earlier items in the same list, the result is false.
* Otherwise, the result is true.
*
* \see setFirstInList(), isLaterInList()
*/
bool isFirstInList ( QTextBlock b );
/** \brief Returns whether the given text block is in a QTextList, but not the first block in it
*
* If \a b is not in a list, the result is false.
* If \a b is in a list and is that list's first block, the result is false.
* Otherwise, the result is true.
*
* \see isFirstInList()
*/
bool isLaterInList ( QTextBlock b );
/** \brief Sets the text block containing \a c to be the first block in a QTextList, if possible
*
* If that block is not in a list, or the list is a bulleted list instead of a numbered list,
* this function does nothing.
* If \a on is true, this function proceeds as follows.
* <ol>
* <li>If the block is already the first block in its list (as per isFirstInList()),
* this function does nothing.</li>
* <li>Otherwise, create a new QTextList with the same format as the list that \a b is
* currently in.</li>
* <li>For each block in the QTextList that the cursor's block is currenly in,
* starting with the cursor's block and
* proceeding from there to the end, remove them from their current list and add them
* to the newly created QTextList.</li>
* </ol>
* If \a on is false, this function proceeds as follows.
* <ol>
* <li>Find an earlier list of the same style and indentation level. If there is not one,
* this function does nothing. In particular, this will be true if the cursor's block
* is <i>not</i> the first item in a list.</li>
* <li>Otherwise, for each block in the QTextList containing the cursor, starting at
* the first item and moving forward, remove them from their current list and add them
* to the earlier list found in the previous step.</li>
* </ol>
* This function uses \a c to do the editing. You may wish
* to call beginEditBlock() beforehand and endEditBlock() afterwards in \a c, because this
* function may perform seveal edits, and does not call those functions.
* If you do not care about having this function use your own cursor for the editing,
* see setFirstInList(QTextBlock,bool); it also calls the begin and end functions for you.
*
* \see isFirstInList(), canSetFirstInList()
*/
void setFirstInList ( QTextCursor& c, bool on = true );
/** \brief A convenience function that calls setFirstInList(QTextCursor,bool) with a new cursor
*
* Creates a cursor in \a b, calls beginEditBlock() in it, calls
* setFirstInList(QTextCursor,bool) using that cursor, and calls endEditBlock() in the cursor.
*/
void setFirstInList ( QTextBlock b, bool on = true );
/** \brief Returns whether the function setFirstInList(\a b,\a on) will take any action
*
* This function can be useful in detecting whether to enable tools for restarting/joining
* numbered lists. See the documentation for setFirstInList(), which begins with several
* situations in which that function would take no action. If any of those situations holds,
* this function returns false; it returns true otherwise.
*
* \see setFirstInList()
*/
bool canSetFirstInList ( QTextBlock b, bool on = true );
/** \brief Whether the text block is part of a numbered list (as opposed to a bulleted list)
*
* If \a b is part of a list and that list is of an enumerated style (using numbers, letters,
* roman numerals, etc., as opposed to bullets, discs, or squares) then this function returns
* true. Otherwise it returns false.
*
* See the Qt documentation for QTextListFormat::Style.
*/
bool isNumberedList ( QTextBlock b );
/** \brief Finds an earlier text block that is in a QTextList that \a b could be joined to, if any
*
* If \a b does not pass the check isFirstInList(), this function returns an invalid block.
*
* If it does pass, then this function iterates backwards through the blocks in the document,
* starting with the one immediately before \a b and proceeding towards the front of the document.
*
* The first time a block is encountered that is a list item with the same indentation level as
* \a b, then one of two actions is taken. If that block's QTextList has a style the same as
* that of \a b, that block is returned, because it is a sensible list to which to join the list
* starting with \a b, should the user choose to do so. But if its style is different, then an
* invalid block is returned, because we can't join our list to that one.
*
* If no such block is encountered, an invalid block is returned.
*
* \see isFirstInList()
*/
QTextBlock previousMatchingList ( QTextBlock b );
/** \brief Ensure a list has the same total indentation level throughout (or break it up)
*
* Several of the routines defined in this file depend upon the assumption that all the blocks
* that share the same QTextList are at the same indentation level (as blocks, and therefore
* the same block-plus-list indentation level).
*
* The user can, of course, mess this up by selecting a subset of such list items and indenting
* it (or unindenting it). Not only does this violate the assumptions of the routines in this
* file, but it is also counterintuitive for the user. Indented levels of an outline (in most
* word processors) use different numbering than the outer levels; they do not keep the same
* numbering flow.
*
* Thus this function can be used to ensure that all the blocks of a list are at the same
* block indentation level. When it encounters blocks that are not at the same indentation level
* as the previous block in the same list, it does not alter the indentation level, but rather
* splits the list, using setFirstInList() with \a b = the second block and \a on = true.
*
* When a user changes the indentation level of a selection, this function can be called on all
* QTextList object relevant to that selection, to preserve the assumption mentioned above.
* A function that does just that is splitListsAtIndentChanges().
*
* \see setFirstInList()
*/
void splitListAtIndentChanges ( QTextList* list );
/** \brief Ensure all lists in the given range have the same total indentation level throughout
*
* For why you may want to ensure such a thing for a QTextList, read the documentation for
* splitListAtIndentChanges(). This function simply finds the set of QTextList objects that
* modify blocks that intersect the range.selection(),
* and applies splitListAtIndentChanges() to each.
*
* \see splitListAtIndentChanges()
*/
void splitListsAtIndentChanges ( QTextCursor range );
/** \brief Return the index converted into text form in the given style
*
* For instance, in the QTextListFormat::ListLowerAlpha style, the first index (zero-based) is "a",
* the second is "b", and so on. If the style instead were QTextListFormat::ListUpperRoman, the
* texts for the indices would be "I", "II", and so on. For bulleted lists, this function just
* returns the string "." as an indicator that a bullet (circle, square, or disc) is shown instead
* of an ordinal indicator.
*/
QString textForIndex ( int index, QTextListFormat::Style style );
/** \brief A class that unites QTextBlock, QTextFrame, and QTextFragment
*
* It is convenient to be able to speak of a node in a QTextDocument tree hierarchy, and yet the
* QTextDocument API does not provide a class for doing so. Instead, it provides QTextFrames,
* which construct most of the hierarchy, QTextBlocks, which are the second-to-lowest level of
* the hierarchy, and QTextFragments, which are the leaves. This class is simply a union of
* those three, for convenience purposes.
*
* At any time, at most one of isFragment(), isBlock(), and isFrame() will be true.
* At construction time, none of the three are true, but isValid() is false.
*/
class QTextNode : public QObject
{
Q_OBJECT
public:
/** \brief Constructs an uninitialized QTextNode
*
* It can be initialized later with operator=().
*/
QTextNode ();
/** \brief Constructs a QTextNode that represents the given fragment
*
* After this, isFragment() will be true and asFragment() will return \a f.
* If \a doc is provided, then this node knows the document in which it sits.
* (QTextFragments do not ordinarily have that information.) Such information is needed
* for functions that deal with this fragment's context, such as nextSibling(), address(),
* parent(), etc.
*/
QTextNode ( QTextFragment f, QTextDocument* doc = NULL );
/** \brief Constructs a QTextNode that represents the given block
*
* After this, isBlock() will be true and asBlock() will return \a b.
*/
QTextNode ( QTextBlock b );
/** \brief Constructs a QTextNode that represents the given frame
*
* After this, isFrame() will be true and asFrame() will return \a f.
* This node will return true from isValid() iff \a f is not NULL.
*/
QTextNode ( QTextFrame* f );
/** \brief Standard copy constructor, which simply uses operator=(QTextNode)
*/
QTextNode ( const QTextNode& other );
/** \brief Test whether this node is a QTextFragment
*/
bool isFragment () const;
/** \brief Test whether this node is a QTextBlock
*/
bool isBlock () const;
/** \brief Test whether this node is a QTextFrame
*/
bool isFrame () const;
/** \brief Test whether this object has been initialized with data
*
* This should always be equivalent to
* <code>isFragment() || isBlock() || isFrame()</code>.
*/
bool isValid () const;
/** \brief Convert this node to a fragment
*
* Returns a valid fragment iff isFragment() returns true.
*/
QTextFragment asFragment () const;
/** \brief Convert this node to a block
*
* Returns a valid block iff isBlock() returns true.
*/
QTextBlock asBlock () const;
/** \brief Convert this node to a frame
*
* Returns a valid fragment iff isFrame() returns true.
*/
QTextFrame* asFrame () const;
/** \brief The QTextDocument containing the block, frame, or fragment this object represents
*
* If this node isBlock() or isFrame(), then the block or frame represented knows its
* document, and that value is returned. Otherwise, if this node isFragment(), then this
* function returns the document provided when this object was constructed, with the
* QTextNode(QTextFragment,QTextDocument*) constructor. If none was provided, NULL is
* returned.
*/
QTextDocument* document () const;
/** \brief The parent node for this node in its document
*
* If this node isFragment(), this function returns a node pointing to the block containing
* that fragment, unless document() returns NULL, in which case this function will return
* an invalid node.
* If this node isBlock() or isFrame(), this function returns a node pointing to the frame
* containing that block or frame. If there is none, for instance if the frame is a
* document's root frame, then this function returns a QTextNode that does not pass
* isValid().
*/
QTextNode parent () const;
/** \brief The number of nodes under this node
*
* If this node isBlock(), this returns the number of fragments it contains.
* If this node isFrame(), this returns the total number of blocks and frames it contains.
* Otherwise, this function returns zero.
*
* \see child()
*/
uint numChildren () const;
/** \brief The node that is the \a ith under this node
*
* If this node isBlock(), this returns its \a ith fragment.
* If this node isFrame(), this returns its \a ith child, where blocks and frames are both
* counted as children, interleaved among one another as in the document. If there is no
* \a ith child, this returns an invalid node.
* If this node isFragment() or !isValid(), this returns an invalid node.
*
* \see numChildren()
*/
QTextNode child ( uint i = 0 ) const;
/** \brief Previous sibling node to this one, if any (invalid node otherwise)
*
* The previous sibling of a fragment will be the preceding fragment in the same block, if
* there is one. The previous sibling of a block or frame will be the preceding block or
* frame in the same parent frame, if there is one. If there is no previous sibling, an
* invalid node is returned. Note that the root frame has no previous sibling.
*
* If the node is invalid or its parent() cannot be computed, this function returns an
* invalid node.
*
* \see nextSibling(), hasNodeBefore(), hasBlockBefore()
*/
QTextNode previousSibling () const;
/** \brief Next sibling node to this one, if any (invalid node otherwise)
*
* The next sibling of a fragment will be the following fragment in the same block, if
* there is one. The next sibling of a block or frame will be the following block or
* frame in the same parent frame, if there is one. If there is no next sibling, an
* invalid node is returned. Note that the root frame has no next sibling.
*
* If the node is invalid or its parent() cannot be computed, this function returns an
* invalid node.
*
* \see previousSibling(), hasNodeAfter(), hasBlockAfter()
*/
QTextNode nextSibling () const;
/** \brief If parent() returns a valid node, this is the index of this node in that parent
*
* Indices are zero-based. Indices in a frame count both blocks and frames in sequence.
* If parent() does not return a valid node, this function returns -1.
*/
int indexInParent () const;
/** \brief A list of unsigned integers is an address into a QTextNode hierarchy
*
* \see convertAddress()
*/
typedef QList<uint> Address;
/** \brief The address of this object in its document
*
* The result is a list of integer indices \f$i_1,\ldots,i_n\f$ so that this node is equal
* to <code>QTextNode( document()->rootFrame() ).child( i_1 ). ... .child( i_n )</code>.
* The list is empty if <code>!isValid() || !parent().isValid()</code>.
*/
Address address () const;
/** \brief Same as address(), but the result is first passed through convertAddress(Address)
*/
LobAddress convertedAddress () const;
/** \brief The node that is at the address \a address under this node
*
* This function iterates the child() function through the entries in \a address, as
* described in the documentation for address(), until a QTextNode is found and returned.
* Note that if \a address does not respect the structure of the hierarchy under this node
* (e.g., asking for child 17 at a time when there are only 8, or asking for a child of
* a fragment) then an invalid QTextNode is returned.
*/
QTextNode index ( const Address& address ) const;
/** \brief Same as index(Address) except the parameter is first passed through
* convertAddress(LobAddress)
*/
QTextNode index ( const LobAddress& address ) const;
/** \brief Changes this object to refer to the fragment \a f
*
* \see operator==(), operator=(QTextBlock), operator=(QTextFrame*), operator=(QTextNode)
*/
QTextNode& operator= ( const QTextFragment& f );
/** \brief Changes this object to refer to the block \a b
*
* \see operator==(), operator=(QTextFragment), operator=(QTextFrame*), operator=(QTextNode)
*/
QTextNode& operator= ( const QTextBlock& b );
/** \brief Changes this object to refer to the frame \a f
*
* \see operator==(), operator=(QTextFragment), operator=(QTextBlock), operator=(QTextNode)
*/
QTextNode& operator= ( QTextFrame* f );
/** \brief Changes this object to refer to the same object to which \a n refers
*
* \see operator==(), operator=(QTextFragment), operator=(QTextBlock), operator=(QTextFrame*)
*/
QTextNode& operator= ( const QTextNode& n );
/** \brief Returns true iff this node and \a other refer to the same block, frame, or fragment
*
* If the two nodes are different types (e.g., one is a block and the other a fragment) then
* this returns false. If either is invalid, this returns false. Otherwise, it returns the
* result of the operator==() function used on the wrapped QTextBlock, QTextFragment, or
* QTextFrame objects.
*
* \see operator=(QTextNode),
* operator=(QTextFragment), operator=(QTextBlock), operator=(QTextFrame*)
*/
bool operator== ( const QTextNode& other ) const;
/** \brief The negation of operator==()
*/
bool operator!= ( const QTextNode& other ) const;
/** \brief Cursor position immediately before the first charcter in this node
*
* If this node is a fragment or block, this is equivalent to its position().
* If this node is a frame, this is equivalent to the position of its first block.
* If this node is invalid, this returns -1.
*
* It is called positionBefore() because cursor positions are always in between characters,
* and this cursor position is immediately before the first character of this node.
*
* \see positionAfter(), positionAtEnd()
*/
int positionBefore () const;
/** \brief Cursor position immediately after the last character in this node
*
* If this node is a fragment or any block other than the last one in the document,
* this is equivalent to its starting position() plus its length().
* If this node is the last block in the document, then this is its starting position()
* plus one less than its length(), because the cursor cannot be placed after the final
* newline of the final block in the document.
* If this node is a frame, then this function returns the positionAfter() that frame's
* final block.
*
* It is called positionAfter() because cursor positions are always in between characters,
* and this cursor position is immediately after the last character of this node.
*
* \see positionBefore(), positionAtEnd()
*/
int positionAfter () const;
/** \brief Cursor position immediately after the last character in this node's contents
*
* If this node is a fragment, then this function behaves exactly like positionAfter().
* If this node is a block or frame, then this function returns the cursor position
* immediately before the final newline of the block (or the last block in the frame).
* In most cases, this is one less than positionAfter(), but if the block in question is
* the last in the document, then the return value of this function is the same as that of
* positionAfter().
*
* \see positionBefore(), positionAfter()
*/
int positionAtEnd () const;
/** \brief Uses the given cursor to select this node
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* The selection runs from positionBefore() to positionAfter().
* If either of those is -1, this function does nothing.
*
* \see select(), goToStart(), goToEnd()
*/
void select ( QTextCursor& cursor );
/** \brief Selects this node with a new cursor and returns it
*
* The selection runs from positionBefore() to positionAfter().
* If either of those is -1, this function returns a newly constructed cursor, unpositioned.
*
* \see select(QTextCursor), goToStart(), goToEnd()
*/
QTextCursor select ();
/** \brief Uses the given cursor to select the contents of this node
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* This is only different from select(QTextCursor) if the node is a block or frame, in which
* case the difference is that the final newline is not included in this selection. If this
* node is or contains the last block in the document, then this operates the same as
* select(QTextCursor).
*
* \see select(QTextCursor), selectContents()
*/
void selectContents ( QTextCursor& cursor );
/** \brief Selects the contents of this node with a new cursor and returns it
*
* This is only different from select() if the node is a block or frame, in which
* case the difference is that the final newline is not included in this selection. If this
* node is or contains the last block in the document, then this operates the same as
* select().
*
* \see select(), selectContents(QTextCursor)
*/
QTextCursor selectContents ();
/** \brief Place \a cursor immediately before the first character of this node
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* Sets the cursor position to positionBefore().
*
* \see goToStart(), goToEnd(), select()
*/
void goToStart ( QTextCursor& cursor );
/** \brief Create a cursor immediately before the first character of this node and return it
*
* The new cursor's position is positionBefore().
*
* \see goToStart(QTextCursor), goToEnd(), select()
*/
QTextCursor goToStart ();
/** \brief Place \a cursor immediately after the last character of this node
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* Sets the cursor position to positionAfter().
*
* \see goToStart(), goToEnd(), select()
*/
void goToEnd ( QTextCursor& cursor );
/** \brief Create a cursor immediately after the last character of this node and return it
*
* The new cursor's position is positionAfter().
*
* \see goToEnd(QTextCursor), goToStart(), select()
*/
QTextCursor goToEnd ();
/** \brief Sets the QTextCharFormat of this node to \a format, if possible
*
* If this node is a fragment, this sets its character format with
* QTextCursor::setCharFormat().
* If this node is a block, this sets its block character format with
* QTextCursor::setBlockCharFormat().
*
* In all other cases, this function does nothing.
*
* \see setFormat(QTextBlockFormat), setFormat(QTextCharFormat,QTextCursor)
*/
void setFormat ( QTextCharFormat format );
/** \brief Sets the QTextCharFormat of this node to \a format, if possible, using \a cursor
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* Acts exactly like setFormat(QTextCharFormat), except uses \a cursor to do the work.
*/
void setFormat ( QTextCharFormat format, QTextCursor& cursor );
/** \brief Sets the QTextBlockFormat of this node to \a format, if possible
*
* If this node is a fragment or block, this sets its block format with
* QTextCursor::setBlockFormat().
*
* In all other cases, this function does nothing.
*
* \see setFormat(QTextCharFormat), setFormat(QTextBlockFormat,QTextCursor)
*/
void setFormat ( QTextBlockFormat format );
/** \brief Sets the QTextBlockFormat of this node to \a format, if possible, using \a cursor
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* Acts exactly like setFormat(QTextBlockFormat), except uses \a cursor to do the work.
*/
void setFormat ( QTextBlockFormat format, QTextCursor& cursor );
/** \brief Remove character formatting from the node
*
* This is equivalent to setFormat( QTextCharFormat() ).
*
* \see setFormat(QTextCharFormat), clearCharFormat(QTextCursor)
*/
void clearCharFormat ();
/** \brief Remove character formatting from the node using \a cursor
*
* This is equivalent to setFormat( QTextCharFormat(), cursor ).
*
* \see setFormat(QTextCharFormat,QTextCursor), clearCharFormat()
*/
void clearCharFormat ( QTextCursor& cursor );
/** \brief Remove block formatting from the node
*
* This is equivalent to setFormat( QTextBlockFormat() ).
*
* \see setFormat(QTextBlockFormat), clearBlockFormat(QTextCursor)
*/
void clearBlockFormat ();
/** \brief Remove block formatting from the node using \a cursor
*
* This is equivalent to setFormat( QTextBlockFormat(), cursor ).
*
* \see setFormat(QTextBlockFormat,QTextCursor), clearBlockFormat()
*/
void clearBlockFormat ( QTextCursor& cursor );
/** \brief Remove character and block formatting from the node
*
* This is equivalent to calling both clearCharFormat() and clearBlockFormat().
*/
void clearFormats ();
/** \brief Remove character and block formatting from the node using \a cursor
*
* This is equivalent to calling both clearCharFormat() and clearBlockFormat() with \a cursor.
*/
void clearFormats ( QTextCursor& cursor );
/** \brief Replace the given node with a text fragment with the given \a text and \a format
*
* If this node is a text fragment, its character format is changed to \a format and its
* contents are changed to \a text. Otherwise, this routine does nothing.
*
* \see append(), insertBefore(), insertAfter(), replaceWithBlock(), replaceWithFrame()
*/
void replace ( QString text, QTextCharFormat format = QTextCharFormat() );
/** \brief Performs the same action as replace(QString,QTextCharFormat), but using \a cursor
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*/
void replace ( QString text, QTextCharFormat format, QTextCursor& cursor );
/** \brief Append a fragment with the given \a text and \a format after this node
*
* If this node is a fragment, the new fragment is added immediately after this one, in the
* same block. If this node is a block, the new fragment is appended to the end of this
* block. Otherwise, this function does nothing.
*
* \see append(QString,QTextCharFormat,QTextCursor), prepend()
*/
void append ( QString text, QTextCharFormat format = QTextCharFormat() );
/** \brief Performs the same action as append(QString,QTextCharFormat), but using \a cursor
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* \see prepend()
*/
void append ( QString text, QTextCharFormat format, QTextCursor& cursor );
/** \brief Insert a fragment with the given \a text and \a format before this node
*
* If this node is a fragment, the new fragment is added immediately before this one, in the
* same block. If this node is a block, the new fragment is inserted at the start of this
* block. Otherwise, this function does nothing.
*
* \see prepend(QString,QTextCharFormat,QTextCursor), append()
*/
void prepend ( QString text, QTextCharFormat format = QTextCharFormat() );
/** \brief Performs the same action as prepend(QString,QTextCharFormat), but using \a cursor
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* \see append()
*/
void prepend ( QString text, QTextCharFormat format, QTextCursor& cursor );
/** \brief Delete the contents of this node
*
* If this node is a block, all its fragments are deleted, leaving an empty block. It will
* have the same block and character formatting (as a block) that it did before; only its
* contents are erased.
*
* If this node is a frame, all its contents except one block are deleted, and that one block
* will have its contents deleted so that it becomes an empty block. That block will also
* have no block or character formatting, even if the first/last block in the frame formerly
* had such formatting; the formatting of the block inside the frame is, for the purposes of
* this function, considered "contents" of the frame.
*
* The notion of "contents" doesn't make as much sence for fragments; if this node is a
* fragment, this function behaves exactly like remove(), including invalidating this node.
*
* \see clearContents(QTextCursor), remove(), isNecessaryBlock()
*/
void clearContents ();
/** \brief Delete the contents of this node, using \a cursor
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* Behaves exactly like clearContents(), but uses \a cursor to do the work.
*
* \see clearContents()
*/
void clearContents ( QTextCursor& cursor );
/** \brief Whether this node is a block that is necessary for the overall document structure
*
* A QTextDocument requires that adjacent to any QTextFrame, on both sides, there must be at
* least one QTextBlock. Also, inside any QTextFrame, there must be at least one QTextBlock.
* Thus there are some blocks in a document that may not be deleted (using the QTextCursor
* API, the only API for altering a QTextDocument, on which the QTextNode class is built).
* Thus it is important to be able to detect when certain nodes in a document are undeletable.
* This function does so by returning true iff the node is a block that cannot be deleted
* for one of the reasons just mentioned.
*
* \see remove()
*/
bool isNecessaryBlock () const;
/** \brief Remove this node from the document, if possible
*
* If isNecessaryBlock() is true about this node, it will not be removed.
* Otherwise, if it is a fragment, it will be deleted from its paragraph. If it is a block,
* it will be deleted from its frame, and if it is a frame, it will be deleted from its parent
* frame. Some of these deletions require careful use of the underlying QTextCursor API, but
* they are all handled carefully by this routine.
*
* If this node's parent() cannot be computed, this function does nothing.
*
* If the function completes successfully, then this object will be invalid.
*
* \see isNecessaryBlock(), remove(QTextCursor), isValid()
*/
void remove ();
/** \brief Remove this node from the document, if possible, using \a cursor
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* Behaves just as remove(), but uses \a cursor to do the work.
* In all cases, \a cursor will then be at the position immediately following the now-absent
* frame (i.e., at the beginning of the block that was the frame's next sibling).
*
* \see remove()
*/
void remove ( QTextCursor& cursor );
/** \brief Insert a block before this node, if possible
*
* This function takes no action if the given node is invalid or a text fragment, or if its
* parent() cannot be computed.
*
* If it is a block or frame with a parent, then a new block is inserted before this block
* or frame. The new block has no character format or block format specified, and no text
* fragments in it.
*
* \see insertBlockBefore(QTextCursor), insertBlockAfter(), replaceWithBlock(),
* insertFrameBefore(), insertFrameAfter(), replaceWithFrame()
*/
void insertBlockBefore ();
/** \brief Insert a block before this node, if possible, using \a cursor
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* Does the same thing as insertBlockBefore(), but uses \a cursor to do the work.
* After the insertion, if successful, \a cursor will be inside the new block.
*
* \see insertBlockBefore(), insertBlockAfter(), replaceWithBlock(),
* insertFrameBefore(), insertFrameAfter(), replaceWithFrame()
*/
void insertBlockBefore ( QTextCursor& cursor );
/** \brief Insert a block after this node, if possible
*
* This function takes no action if the given node is invalid or a text fragment, or if its
* parent() cannot be computed.
*
* If it is a block or frame with a parent, then a new block is inserted after this block
* or frame. The new block has no character format or block format specified, and no text
* fragments in it.
*
* \see insertBlockAfter(QTextCursor), insertBlockBefore(), replaceWithBlock(),
* insertFrameBefore(), insertFrameAfter(), replaceWithFrame()
*/
void insertBlockAfter ();
/** \brief Insert a block after this node, if possible, using \a cursor
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* Does the same thing as insertBlockAfter(), but uses \a cursor to do the work.
* After the insertion, if successful, \a cursor will be inside the new block.
*
* \see insertBlockBefore(), insertBlockAfter(), replaceWithBlock(),
* insertFrameBefore(), insertFrameAfter(), replaceWithFrame()
*/
void insertBlockAfter ( QTextCursor& cursor );
/** \brief Replace this node with an empty block, if possible
*
* This function takes no action if the given node is invalid or a text fragment, or if its
* parent() cannot be computed.
*
* If it is a block or frame with a parent, then a new block is inserted to replace this
* block or frame. The new block has no character format or block format specified, and no
* text fragments in it. (For a block, this is equivalent to clearContents() followed by
* clearFormats().)
*
* \see replaceWithBlock(QTextCursor), insertBlockAfter(), insertBlockBefore(),
* insertFrameBefore(), insertFrameAfter(), replaceWithFrame()
*/
void replaceWithBlock ();
/** \brief Replace this node with an empty block, if possible, using \a cursor
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* Does the same thing as replaceWithBlock(), but uses \a cursor to do the work.
* After the insertion, if successful, \a cursor will be inside the new block.
*
* \see insertBlockBefore(), insertBlockAfter(), replaceWithBlock(),
* insertFrameBefore(), insertFrameAfter(), replaceWithFrame()
*/
void replaceWithBlock ( QTextCursor& cursor );
/** \brief Insert a frame before this node, if possible
*
* This function takes no action unless this node is a block whose previous sibling is also
* a block. To insert a frame before another frame, you must first insert a block before the
* existing frame, and then insert a frame before that.
*
* If the above checks pass, then a new frame is inserted before this block, after its
* previous sibling.
* The new frame has in it just one block, unformatted and empty.
*
* The new frame has format \a format.
*
* \see insertBlockBefore(), insertBlockAfter(), replaceWithBlock(),
* insertFrameBefore(QTextCursor,QTextFrameFormat), insertFrameAfter(), replaceWithFrame()
*/
void insertFrameBefore ( QTextFrameFormat format = QTextFrameFormat() );
/** \brief Insert a frame before this node, if possible, using \a cursor
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* Does the same thing as insertFrameBefore(), but uses \a cursor to do the work.
* After the insertion, if successful, \a cursor will be inside the one block in the new
* frame.
*
* \see insertBlockBefore(), insertBlockAfter(), replaceWithBlock(),
* insertFrameBefore(), insertFrameAfter(), replaceWithFrame()
*/
void insertFrameBefore ( QTextCursor& cursor, QTextFrameFormat format = QTextFrameFormat() );
/** \brief Insert a frame after this node, if possible
*
* This function takes no action unless this node is a block whose next sibling is also
* a block. To insert a frame after another frame, you must first insert a block after the
* existing frame, and then insert a frame after that.
*
* If the above checks pass, then a new frame is inserted after this block, before its
* next sibling.
* The new frame has in it just one block, unformatted and empty.
*
* \see insertBlockAfter(), insertBlockBefore(), replaceWithBlock(),
* insertFrameAfter(QTextCursor,QTextFrameFormat), insertFrameBefore(), replaceWithFrame()
*/
void insertFrameAfter ( QTextFrameFormat format = QTextFrameFormat() );
/** \brief Insert a frame after this node, if possible, using \a cursor
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* Does the same thing as insertFrameAfter(), but uses \a cursor to do the work.
* After the insertion, if successful, \a cursor will be inside the one block in the new
* frame.
*
* \see insertBlockBefore(), insertBlockAfter(), replaceWithBlock(),
* insertFrameBefore(), insertFrameAfter(), replaceWithFrame()
*/
void insertFrameAfter ( QTextCursor& cursor, QTextFrameFormat format = QTextFrameFormat() );
/** \brief Replace this node with a new frame, if possible
*
* This function takes no action if the given node is invalid or a text fragment, or if its
* parent() cannot be computed. It also takes no action if this node is a block that does
* not have blocks both before and after it. This can be tested (and optionally remedied)
* by the caller using code like the following.
* \code
* if ( !myNode.previousSibling().isBlock() )
* myNode.insertBlockBefore();
* if ( !myNode.nextSibling().isBlock() )
* myNode.insertBlockAfter();
* myNode.replaceWithFrame();
* \endcode
*
* If none of the above criteria prevent it, then a new frame is inserted to replace this
* block or frame. The new frame has in it just one block, unformatted and empty.
* (For a frame, this is equivalent to clearContents().)
*
* \see insertBlockAfter(), insertBlockBefore(), replaceWithBlock(),
* insertFrameBefore(), insertFrameAfter(), replaceWithFrame(QTextCursor,QTextFrameFormat)
*/
void replaceWithFrame ( QTextFrameFormat format = QTextFrameFormat() );
/** \brief Replace this node with a new frame, if possible, using \a cursor
*
* If the cursor is null or its
* document() is not equal to this node's document(), then this function
* does nothing.
*
* Does the same thing as replaceWithFrame(), but uses \a cursor to do the work.
* After the insertion, if successful, \a cursor will be inside the one block in the new
* frame.
*
* \see insertBlockBefore(), insertBlockAfter(), replaceWithBlock(),
* insertFrameBefore(), insertFrameAfter(), replaceWithFrame()
*/
void replaceWithFrame ( QTextCursor& cursor, QTextFrameFormat format = QTextFrameFormat() );
/** \brief Converts a LobAddress into a QTextNode hierarchy address
*
* For a LobAddress whose steps are all OmNode::AsChild steps, this converts each step into
* an integer (using its stepIndex()) and subtracts one from it. This is because Lob
* hierarchies that imitate QTextNode hierarchies have OpenMath symbols in them as their
* 0th indices, which do not exist in the QTextNode hierarchy. The resulting Address
* is returned.
*
* An empty list is returned in one of three cases.
* <ul>
* <li>if the LobAddress had no steps</li>
* <li>if the LobAddress was not exclusively composed of child steps</li>
* <li>if the LobAddress had any c0 steps, so that the subtraction of 1 cannot be done</li>
* </ul>
*/
static Address convertAddress ( const LobAddress& address );
/** \brief Converts a QTextNode hierarchy address into a LobAddress
*
* For an Address whose entries are \f$i_1,\ldots,i_n\f$, this creates a LobAddress
* whose \a jth step is an OmNode::AsChild step with stepIndex() equal to \f$i_j+1\f$.
* This is therefore a kind of inverse to convertAddress(LobAddress).
*/
static LobAddress convertAddress ( const Address& address );
/** \brief A simple string representation, mostly useful for debugging
*/
QString toString () const;
private slots:
/** \brief Notices when a wrapped QTextFrame is destroyed, and invalidates this object
*
* If this node refers to a QTextFrame, and that frame is destroyed, it is essential for this
* object to notice that fact and invalidate its internal pointer to the frame (thus making
* this node invalid). Otherwise, this object may attempt to call functions in the
* no-longer-existing frame, and result in the application's crashing.
*
* Noticing the frame's destruction is accomplished by connecting the frame's destroyed()
* signal to this slot.
*
* \see setFrame(), docWasDestroyed()
*/
void frameWasDestroyed ();
/** \brief Notices when this node's document is destroyed, and invalidates this object
*
* If this node knows about its document, and that document is destroyed, it is essential for
* this object to notice that fact and invalidate itself. Otherwise, this object may attempt
* to look up data in the document (using asBlock().text() or asFragment().text(), for
* example) and this has been known to cause crashes/hangs.
*
* Noticing the document's destruction is accomplished by connecting the document's
* destroyed() signal to this slot.
*
* \see setDoc(), frameWasDestroyed()
*/
void docWasDestroyed ();
private:
/** \brief Auxiliary routine that does the work of index(Address).
*/
QTextNode index ( const Address& address, int step ) const;
/** \brief Create a new QTextCursor on the document in which this node sits
*
* If there is no known document, return a default-constructed cursor.
*/
QTextCursor newCursor ();
/** \brief Stores \a f in this object's \a frame field, and sets up a frameWasDestroyed()
* connection
*
* This function proceeds as follows.
* <ol>
* <li>Assign \a f to \a frame.</li>
* <li>Disconnect any former connection to the frameWasDestroyed() slot.</li>
* <li>If \a frame is non-null, set up the connection documented in frameWasDestroyed().</li>
* </ol>
*/
void setFrame ( QTextFrame* f );
/** \brief Stores \a d in this object's \a doc field, and sets up a docWasDestroyed()
* connection
*
* This function proceeds as follows.
* <ol>
* <li>Assign \a d to \a doc.</li>
* <li>Disconnect any former connection to the docWasDestroyed() slot.</li>
* <li>If \a doc is non-null, set up the connection documented in docWasDestroyed().</li>
* </ol>
*/
void setDoc ( QTextDocument* d );
/** \brief This node is a fragment iff this object is valid.
*/
QTextFragment fragment;
/** \brief This node is a block iff this object is valid.
*/
QTextBlock block;
/** \brief This node is a frame iff this pointer is non-null.
*/
QTextFrame* frame;
/** \brief If this node is a fragment, this pointer may refer to its document.
*
* Or it may be null, if that information is not available.
*/
QTextDocument* doc;
};
#endif // QTEXTDOCUMENTUTILS_H