~ubuntu-branches/ubuntu/precise/ipe/precise

« back to all changes in this revision

Viewing changes to src/ipelib/ipepage.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Steve M. Robbins
  • Date: 2004-06-08 00:44:02 UTC
  • Revision ID: james.westby@ubuntu.com-20040608004402-72yu51xlh7vt6p9m
Tags: upstream-6.0pre16
ImportĀ upstreamĀ versionĀ 6.0pre16

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// --------------------------------------------------------------------
 
2
// A page of a document.
 
3
// --------------------------------------------------------------------
 
4
/*
 
5
 
 
6
    This file is part of the extensible drawing editor Ipe.
 
7
    Copyright (C) 1993-2004  Otfried Cheong
 
8
 
 
9
    Ipe is free software; you can redistribute it and/or modify it
 
10
    under the terms of the GNU General Public License as published by
 
11
    the Free Software Foundation; either version 2 of the License, or
 
12
    (at your option) any later version.
 
13
 
 
14
    As a special exception, you have permission to link Ipe with the
 
15
    CGAL library and distribute executables, as long as you follow the
 
16
    requirements of the Gnu General Public License in regard to all of
 
17
    the software in the executable aside from CGAL.
 
18
 
 
19
    Ipe is distributed in the hope that it will be useful, but WITHOUT
 
20
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 
21
    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
 
22
    License for more details.
 
23
 
 
24
    You should have received a copy of the GNU General Public License
 
25
    along with Ipe; if not, you can find it at
 
26
    "http://www.gnu.org/copyleft/gpl.html", or write to the Free
 
27
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
28
 
 
29
*/
 
30
 
 
31
#include "ipepage.h"
 
32
#include "ipevisitor.h"
 
33
#include "ipegroup.h"
 
34
#include "ipepath.h"
 
35
#include "ipetext.h"
 
36
#include "ipemark.h"
 
37
#include "ipepainter.h"
 
38
#include "ipeiml.h"
 
39
#include "ipeutils.h"
 
40
 
 
41
// --------------------------------------------------------------------
 
42
 
 
43
/*! \class IpeLayer
 
44
  \ingroup doc
 
45
  \brief A layer of an IpePage.
 
46
 
 
47
  The objects on one IpePage can belong to any number of layers.
 
48
  Layers are orthogonal to the back-to-front ordering of objects, so a
 
49
  "layer" is just another attribute of the object.
 
50
 
 
51
  Layers have several attributes:
 
52
 
 
53
  - They may be editable or locked.  Objects in a locked layer cannot
 
54
    be selected, and a locked layer cannot be made active in the Ipe
 
55
    UI.  This more or less means that the contents of such a layer
 
56
    cannot be modified---but that's a consequence of the UI, Ipelib
 
57
    contains no special handling of locked layers.
 
58
 
 
59
  - A layer may be visible, invisible, or dimmed.
 
60
 
 
61
  - A layer may have snapping on or off---objects will behave
 
62
    magnetically only if their layer has snapping on.
 
63
 
 
64
  The PDF output generated for an IpePage depends on its \e
 
65
  views. Each view may list a number of layers to be displayed at that
 
66
  stage.  Multiple \e views may show different subsets of layers.
 
67
*/
 
68
 
 
69
//! Construct with name. Default attributes.
 
70
IpeLayer::IpeLayer(IpeString name)
 
71
{
 
72
  iName = name;
 
73
  iFlags = 0;
 
74
}
 
75
 
 
76
//! Construct from a single XML tag.
 
77
IpeLayer::IpeLayer(const IpeXmlAttributes &attr)
 
78
{
 
79
  iName = attr["name"];
 
80
  iFlags = 0;
 
81
  IpeString str;
 
82
  if (attr.Has("visible", str)) {
 
83
    if (str == "dim")
 
84
      iFlags |= EDim;
 
85
    else if (str == "no")
 
86
      iFlags |= EHidden;
 
87
  }
 
88
  if (attr.Has("edit", str) && str == "no")
 
89
    iFlags |= ELocked;
 
90
}
 
91
 
 
92
//! Write a single XML tag describing this layer.
 
93
void IpeLayer::SaveAsXml(IpeStream &stream) const
 
94
{
 
95
  stream << "<layer name=\"" << iName << "\"";
 
96
  switch (iFlags & (EHidden|EDim)) {
 
97
  case 0:
 
98
    break;
 
99
  case EDim:
 
100
    stream << " visible=\"dim\"";
 
101
    break;
 
102
  case EHidden:
 
103
    stream << " visible=\"no\"";
 
104
    break;
 
105
  }
 
106
  if (iFlags & ELocked)
 
107
    stream << " edit=\"no\"";
 
108
  stream << "/>\n";
 
109
}
 
110
 
 
111
//! Set visibility.
 
112
void IpeLayer::SetVisible(bool flag)
 
113
{
 
114
  iFlags &= ~EHidden;
 
115
  if (!flag) iFlags |= EHidden;
 
116
}
 
117
 
 
118
//! Set dimming.
 
119
void IpeLayer::SetDimmed(bool flag)
 
120
{
 
121
  iFlags &= ~EDim;
 
122
  if (flag) iFlags |= EDim;
 
123
}
 
124
 
 
125
//! Set locking.
 
126
void IpeLayer::SetLocked(bool flag)
 
127
{
 
128
  iFlags &= ~ELocked;
 
129
  if (flag) iFlags |= ELocked;
 
130
}
 
131
 
 
132
//! Set snapping.
 
133
void IpeLayer::SetSnapping(bool flag)
 
134
{
 
135
  iFlags &= ~ENoSnapping;
 
136
  if (!flag) iFlags |= ENoSnapping;
 
137
}
 
138
 
 
139
// --------------------------------------------------------------------
 
140
 
 
141
/*! \class IpeView
 
142
  \ingroup doc
 
143
  \brief A view of the page (set of layers, duration, effect, transition style)
 
144
 
 
145
  An IpePage contains a whole list of these.
 
146
*/
 
147
 
 
148
//! Construct default view.
 
149
IpeView::IpeView()
 
150
{
 
151
  iEffect = ENormal;
 
152
  iDuration = 0;
 
153
  iTransitionTime = 1;
 
154
}
 
155
 
 
156
//! Create a view from XML tag.
 
157
IpeView::IpeView(const IpeXmlAttributes &attr)
 
158
{
 
159
  iEffect = ENormal;
 
160
  iDuration = 0;
 
161
  iTransitionTime = 1;
 
162
  IpeLex st(attr["layers"]);
 
163
  do {
 
164
    iLayers.push_back(st.NextToken());
 
165
    st.SkipWhitespace();
 
166
  } while (!st.Eos());
 
167
  iActive = attr["active"];
 
168
  IpeString str;
 
169
  if (attr.Has("duration", str))
 
170
    iDuration = IpeLex(str).GetInt();
 
171
  if (attr.Has("transition", str))
 
172
    iTransitionTime = IpeLex(str).GetInt();
 
173
  if (attr.Has("effect", str))
 
174
    iEffect = TEffect(IpeLex(str).GetInt());
 
175
}
 
176
 
 
177
//! Write a single XML tag representing this view.
 
178
void IpeView::SaveAsXml(IpeStream &stream) const
 
179
{
 
180
  stream << "<view layers=\"";
 
181
  for (std::vector<IpeString>::const_iterator it = iLayers.begin();
 
182
       it != iLayers.end(); ++it) {
 
183
    if (it != iLayers.begin())
 
184
      stream << " ";
 
185
    stream << *it;
 
186
  }
 
187
  stream << "\"";
 
188
  if (!iActive.empty())
 
189
    stream << " active=\"" << iActive << "\"";
 
190
  if (iDuration > 0)
 
191
    stream << " duration=\"" << iDuration << "\"";
 
192
  if (iEffect != ENormal)
 
193
    stream << " effect=\"" << int(iEffect) << "\"";
 
194
  if (iTransitionTime > 1)
 
195
    stream << " transition=\"" << iTransitionTime << "\"";
 
196
  stream << "/>\n";
 
197
}
 
198
 
 
199
 
 
200
//! Write part of page dictionary.
 
201
/*! Write part of page dictionary indicating effect,
 
202
  including the two keys /Dur and /Trans. */
 
203
void IpeView::PageDictionary(IpeStream &stream) const
 
204
{
 
205
  if (iDuration > 0)
 
206
    stream << "/Dur " << iDuration << "\n";
 
207
  if (iEffect != ENormal) {
 
208
    stream << "/Trans << /D " << iTransitionTime << " /S ";
 
209
    switch (iEffect) {
 
210
    case ESplitHI: stream << "/Split /Dm /H /M /I"; break;
 
211
    case ESplitHO: stream << "/Split /Dm /H /M /O"; break;
 
212
    case ESplitVI: stream << "/Split /Dm /V /M /I"; break;
 
213
    case ESplitVO: stream << "/Split /Dm /V /M /O"; break;
 
214
    case EBlindsH: stream << "/Blinds /Dm /H"; break;
 
215
    case EBlindsV: stream << "/Blinds /Dm /V"; break;
 
216
    case EBoxI: stream << "/Box /M /I"; break;
 
217
    case EBoxO: stream << "/Box /M /O"; break;
 
218
    case EWipeLR: stream << "/Wipe /Di 0"; break;
 
219
    case EWipeBT: stream << "/Wipe /Di 90"; break;
 
220
    case EWipeRL: stream << "/Wipe /Di 180"; break;
 
221
    case EWipeTB: stream << "/Wipe /Di 270"; break;
 
222
    case EDissolve: stream << "/Dissolve"; break;
 
223
    case EGlitterLR: stream << "/Glitter /Di 0"; break;
 
224
    case EGlitterTB: stream << "/Glitter /Di 270"; break;
 
225
    case EGlitterD: stream << "/Glitter /Di 315"; break;
 
226
    case ENormal: break; // to satisfy compiler
 
227
    }
 
228
    stream << " >>\n";
 
229
  }
 
230
}
 
231
 
 
232
// --------------------------------------------------------------------
 
233
 
 
234
/*! \class IpePage
 
235
  \ingroup doc
 
236
  \brief An Ipe document page.
 
237
 
 
238
  Its main ingredients are a sequence of IpePgObjects, a list of
 
239
  IpeLayers, and a list of IpeViews.
 
240
 
 
241
  If you need to keep track of whether a document has been modified,
 
242
  you have to call SetEdited(true) whenever you modify an IpePgObject.
 
243
 
 
244
  The functions to modify the layer sequence and the views set the
 
245
  edited flag themselves.
 
246
*/
 
247
 
 
248
//! The default constructor creates a new empty page.
 
249
IpePage::IpePage()
 
250
{
 
251
  iEdited = false;
 
252
  iGridSize = 0.0;
 
253
}
 
254
 
 
255
// --------------------------------------------------------------------
 
256
 
 
257
//! Save page in XML format.
 
258
void IpePage::SaveAsXml(IpePainter &painter, IpeStream &stream) const
 
259
{
 
260
  stream << "<page";
 
261
  if (iGridSize > 0.0)
 
262
    stream << " gridsize=\"" << iGridSize << "\"";
 
263
  stream << ">\n";
 
264
  for (int i = 0; i < CountLayers(); ++i) {
 
265
    Layer(i).SaveAsXml(stream);
 
266
  }
 
267
  for (IpeViewSeq::const_iterator it = iViews.begin();
 
268
       it != iViews.end(); ++it) {
 
269
    it->SaveAsXml(stream);
 
270
  }
 
271
  int currentLayer = -1;
 
272
  for (const_iterator it = begin(); it != end(); ++it) {
 
273
    IpeString layer;
 
274
    if (it->Layer() != currentLayer) {
 
275
      currentLayer = it->Layer();
 
276
      layer = Layer(currentLayer).iName;
 
277
    }
 
278
    it->Object()->SaveAsXml(painter, stream, layer);
 
279
  }
 
280
  stream << "</page>\n";
 
281
}
 
282
 
 
283
//! Modify a layer.
 
284
/*! Sets the edited flag. */
 
285
void IpePage::SetLayer(int index, const IpeLayer &layer)
 
286
{
 
287
  iLayers[index] = layer;
 
288
  iEdited = true;
 
289
}
 
290
 
 
291
//! Add a new layer at \a index (at the end if \a index is negative).
 
292
/*! Returns index of new layer, and sets edited flag.
 
293
  Layer numbers of all objects on page are adjusted if necessary.
 
294
 */
 
295
int IpePage::AddLayer(const IpeLayer &layer, int index)
 
296
{
 
297
  iEdited = true;
 
298
  if (index < 0) {
 
299
    iLayers.push_back(layer);
 
300
    return (iLayers.size() - 1);
 
301
  } else {
 
302
    IpeLayerSeq::iterator it = iLayers.begin() + index;
 
303
    iLayers.insert(it, layer);
 
304
    // adjust all objects!
 
305
    for (iterator pit = begin(); pit != end(); ++pit) {
 
306
      int l = pit->Layer();
 
307
      if (l >= index)
 
308
        pit->SetLayer(l + 1);
 
309
    }
 
310
    return index;
 
311
  }
 
312
}
 
313
 
 
314
//! Find layer with given name.
 
315
/*! Returns -1 if not found. */
 
316
int IpePage::FindLayer(IpeString name) const
 
317
{
 
318
  for (int i = 0; i < CountLayers(); ++i)
 
319
    if (Layer(i).Name() == name)
 
320
      return i;
 
321
  return -1;
 
322
}
 
323
 
 
324
const char * const layerNames[] = {
 
325
  "alpha", "beta", "gamma", "delta", "epsilon", "zeta", "eta",
 
326
  "theta", "iota", "kappa", "lambda", "mu", "nu", "xi",
 
327
  "omicron", "pi", "rho", "sigma", "tau", "phi", "chi", "xi",
 
328
  "omega" };
 
329
 
 
330
//! Create a new layer with unique name.
 
331
/*! The layer is inserted at index \a index, or appended if
 
332
  \a index is negative.
 
333
  Returns index of new layer. */
 
334
int IpePage::NewLayer(int index)
 
335
{
 
336
  iEdited = true;
 
337
  for (int i = 0; i < int(sizeof(layerNames)/sizeof(const char *)); ++i) {
 
338
    if (FindLayer(layerNames[i]) < 0)
 
339
      return AddLayer(IpeLayer(layerNames[i]), index);
 
340
  }
 
341
  char name[20];
 
342
  int i = 1;
 
343
  for (;;) {
 
344
    std::sprintf(name, "alpha%d", i);
 
345
    if (FindLayer(name) < 0)
 
346
      return AddLayer(IpeLayer(name), index);
 
347
    i++;
 
348
  }
 
349
}
 
350
 
 
351
//! Deletes an empty layer from the page.
 
352
/*! All objects are adjusted.  Panics if there are objects in the
 
353
  deleted layer, of if it is the only layer.
 
354
  The layer is also removed from all views.
 
355
*/
 
356
void IpePage::DeleteLayer(int index)
 
357
{
 
358
  assert(iLayers.size() > 1);
 
359
  for (iterator it = begin(); it != end(); ++it) {
 
360
    int k = it->Layer();
 
361
    assert(k != index);
 
362
    if (k > index)
 
363
      it->SetLayer(k-1);
 
364
  }
 
365
 
 
366
  // remove from all views
 
367
  IpeString name = iLayers[index].Name();
 
368
  for (int i = 0; i < CountViews(); ++i) {
 
369
    IpeView &view = iViews[i];
 
370
    for (std::vector<IpeString>::iterator it = view.iLayers.begin();
 
371
         it != view.iLayers.end(); ++it) {
 
372
      if (*it == name) {
 
373
        view.iLayers.erase(it);
 
374
        break;
 
375
      }
 
376
    }
 
377
  }
 
378
 
 
379
  iLayers.erase(iLayers.begin() + index);
 
380
  iEdited = true;
 
381
}
 
382
 
 
383
//! Does a view exist where this layer is active?
 
384
bool IpePage::IsLayerActiveInView(int index) const
 
385
{
 
386
  IpeString name = iLayers[index].Name();
 
387
  for (int i = 0; i < CountViews(); ++i)
 
388
    if (iViews[i].iActive == name)
 
389
      return true;
 
390
  return false;
 
391
}
 
392
 
 
393
//! Set the views for the page.
 
394
/*! This sets the edited flag. */
 
395
void IpePage::SetViews(const IpeViewSeq &views)
 
396
{
 
397
  iViews = views;
 
398
  iEdited = true;
 
399
}
 
400
 
 
401
//! Sets one view of the page.
 
402
/*! This sets the edited flag. */
 
403
void IpePage::SetView(int index, const IpeView &view)
 
404
{
 
405
  iViews[index] = view;
 
406
  iEdited = true;
 
407
}
 
408
 
 
409
//! Add a view at position \a index.
 
410
/*! The view is appended at the end if \a index is negative. */
 
411
void IpePage::AddView(const IpeView &view, int index)
 
412
{
 
413
  if (index < 0)
 
414
    iViews.push_back(view);
 
415
  else
 
416
    iViews.insert(iViews.begin() + index, view);
 
417
  iEdited = true;
 
418
}
 
419
 
 
420
//! Delete view at \a index.
 
421
void IpePage::DeleteView(int index)
 
422
{
 
423
  iViews.erase(iViews.begin() + index);
 
424
  iEdited = true;
 
425
}
 
426
 
 
427
class TextBoxVisitor : public IpeVisitor {
 
428
public:
 
429
  virtual void VisitText(const IpeText *obj);
 
430
public:
 
431
  IpeRect iR;
 
432
  IpeScalar iY;
 
433
};
 
434
 
 
435
void TextBoxVisitor::VisitText(const IpeText *t)
 
436
{
 
437
  // look at all minipage objects that span the same horizontal extent
 
438
  if (t->Type() == IpeText::ETextbox) {
 
439
    IpeScalar bottom = (t->Matrix() * t->Position()).iY - t->TotalHeight();
 
440
    if (bottom < iY)
 
441
      iY = bottom;
 
442
  }
 
443
}
 
444
 
 
445
//! Computes text box.
 
446
/*! Takes into account media size, margins, and text objects already
 
447
  on the page .*/
 
448
IpeRect IpePage::TextBox(const IpeRect &media,
 
449
                         const IpeStyleSheet *sheet) const
 
450
{
 
451
  IpeVector tl, br;
 
452
  sheet->FindMargins(tl, br);
 
453
  TextBoxVisitor vis;
 
454
  vis.iR = IpeRect(media.Min() + IpeVector(tl.iX, br.iY),
 
455
                   media.Max() - IpeVector(br.iX, tl.iY));
 
456
  vis.iY = vis.iR.Max().iY;
 
457
  for (const_iterator it = begin(); it != end(); ++it)
 
458
    vis(*it);
 
459
  return IpeRect(vis.iR.Min(), IpeVector(vis.iR.Max().iX, vis.iY));
 
460
}
 
461
 
 
462
//! Set whether page has been edited.
 
463
void IpePage::SetEdited(bool edited)
 
464
{
 
465
  iEdited = edited;
 
466
}
 
467
 
 
468
//! Returns true iff any object on the page is selected.
 
469
bool IpePage::HasSelection() const
 
470
{
 
471
  for (const_iterator it = begin(); it != end(); ++it) {
 
472
    if (it->Select() != IpePgObject::ENone)
 
473
      return true;
 
474
  }
 
475
  return false;
 
476
}
 
477
 
 
478
//! Returns the primary selection, or end().
 
479
IpePage::iterator IpePage::PrimarySelection()
 
480
{
 
481
  for (iterator it = begin(); it != end(); ++it) {
 
482
    if (it->Select() == IpePgObject::EPrimary)
 
483
      return it;
 
484
  }
 
485
  return end();
 
486
}
 
487
 
 
488
//! Deselect all objects.
 
489
void IpePage::DeselectAll()
 
490
{
 
491
  for (iterator it = begin(); it != end(); ++it)
 
492
    it->SetSelect(IpePgObject::ENone);
 
493
}
 
494
 
 
495
//! Deselect all objects in this layer.
 
496
void IpePage::DeselectLayer(int layer)
 
497
{
 
498
  for (iterator it = begin(); it != end(); ++it)
 
499
    if (it->Layer() == layer)
 
500
      it->SetSelect(IpePgObject::ENone);
 
501
}
 
502
 
 
503
//! Deselect all objects not in a layer of this view.
 
504
void IpePage::DeselectNotInView(int view)
 
505
{
 
506
  if (CountViews() > 0) {
 
507
    // make a table first
 
508
    const std::vector<IpeString> &layerNames = iViews[view].iLayers;
 
509
    std::vector<bool> layer;
 
510
    for (int i = 0; i < CountLayers(); ++i) {
 
511
      bool inView
 
512
        = (std::find(layerNames.begin(), layerNames.end(), iLayers[i].Name())
 
513
           != layerNames.end());
 
514
      layer.push_back(inView);
 
515
    }
 
516
    // now deselect all objects
 
517
    for (iterator it = begin(); it != end(); ++it)
 
518
      if (!layer[it->Layer()])
 
519
        it->SetSelect(IpePgObject::ENone);
 
520
  }
 
521
}
 
522
 
 
523
/*! If no object is the primary selection, make the topmost secondary
 
524
  selection the primary one. */
 
525
void IpePage::EnsurePrimarySelection()
 
526
{
 
527
  for (IpePage::const_iterator it = begin(); it != end(); ++it) {
 
528
    if (it->Select() == IpePgObject::EPrimary)
 
529
      return;
 
530
  }
 
531
  IpePage::reverse_iterator it = rbegin();
 
532
  while (it != rend() && it->Select() != IpePgObject::ESecondary)
 
533
    ++it;
 
534
  if (it != rend())
 
535
    it->SetSelect(IpePgObject::EPrimary);
 
536
}
 
537
 
 
538
//! Removes all selected objects from the page.
 
539
/*! They are added, in the same order, to \a seq. */
 
540
void IpePage::ExtractSelection(IpePgObjectSeq &seq)
 
541
{
 
542
  iterator it1;
 
543
  for (iterator it = begin(); it != end(); it = it1) {
 
544
    it1 = it;
 
545
    ++it1;
 
546
    if (it->Select() != IpePgObject::ENone) {
 
547
      seq.push_back(*it);
 
548
      erase(it);
 
549
      iEdited = true;
 
550
    }
 
551
  }
 
552
}
 
553
 
 
554
//! Delete currently selected objects
 
555
void IpePage::Delete()
 
556
{
 
557
  iterator it1;
 
558
  for (iterator it = begin(); it != end(); it = it1) {
 
559
    it1 = it;
 
560
    ++it1;
 
561
    if (it->Select() != IpePgObject::ENone) {
 
562
      erase(it);
 
563
      iEdited = true;
 
564
    }
 
565
  }
 
566
}
 
567
 
 
568
//! Select all objects on the page.
 
569
void IpePage::SelectAll()
 
570
{
 
571
  for (iterator it = begin(); it != end(); ++it) {
 
572
    if (!it->Select())
 
573
      it->SetSelect(IpePgObject::ESecondary);
 
574
  }
 
575
  EnsurePrimarySelection();
 
576
}
 
577
 
 
578
//! Select all objects in the given layer.
 
579
void IpePage::SelectAllInLayer(int layer)
 
580
{
 
581
  DeselectAll();
 
582
  IpePgObject::TSelect sel = IpePgObject::EPrimary;
 
583
  for (iterator it = begin(); it != end(); ++it) {
 
584
    if (it->Layer() == layer) {
 
585
      it->SetSelect(sel);
 
586
      sel = IpePgObject::ESecondary;
 
587
    }
 
588
  }
 
589
}
 
590
 
 
591
//! Group the selected objects together as a new group object.
 
592
/*! The new object is placed in \a layer. */
 
593
void IpePage::Group(int layer)
 
594
{
 
595
  IpePgObjectSeq objs;
 
596
  ExtractSelection(objs);
 
597
  IpeGroup *group = new IpeGroup;
 
598
  for (IpePgObjectSeq::iterator it = objs.begin(); it != objs.end(); ++it)
 
599
    group->push_back(it->Object()->Clone());
 
600
  push_back(IpePgObject(IpePgObject::EPrimary, layer, group));
 
601
  iEdited = true;
 
602
}
 
603
 
 
604
//! Move selected objects to indicated layer.
 
605
void IpePage::MoveToLayer(int layer)
 
606
{
 
607
  for (iterator it = begin(); it != end(); ++it) {
 
608
    if (it->Select())
 
609
      it->SetLayer(layer);
 
610
  }
 
611
  iEdited = true;
 
612
}
 
613
 
 
614
//! Ungroup the primary selection, place objects in \a layer.
 
615
/*! Panics if no primary selection, returns false if primary selection
 
616
  is not a group. */
 
617
bool IpePage::Ungroup(int layer)
 
618
{
 
619
  iterator it = PrimarySelection();
 
620
  const IpeGroup *group = it->Object()->AsGroup();
 
621
  if (!group)
 
622
    return false;
 
623
  for (IpeGroup::const_iterator it1 = group->begin();
 
624
       it1 != group->end(); ++it1) {
 
625
    IpeObject *obj = (*it1)->Clone();
 
626
    // apply attributes from group
 
627
    if (group->Stroke())
 
628
      obj->SetStroke(group->Stroke());
 
629
    obj->SetMatrix(group->Matrix() * obj->Matrix());
 
630
    IpeFillable *fobj = obj->AsFillable();
 
631
    if (fobj) {
 
632
      if (group->Fill())
 
633
        fobj->SetFill(group->Fill());
 
634
      if (group->LineWidth())
 
635
        fobj->SetLineWidth(group->LineWidth());
 
636
      if (group->DashStyle())
 
637
        fobj->SetDashStyle(group->DashStyle());
 
638
    }
 
639
    if (group->TextSize()) {
 
640
      if (obj->AsText())
 
641
        obj->AsText()->SetSize(group->TextSize());
 
642
      else if (obj->AsGroup())
 
643
        obj->AsGroup()->SetTextSize(group->TextSize());
 
644
    }
 
645
    if (group->MarkSize()) {
 
646
      if (obj->AsMark())
 
647
        obj->AsMark()->SetSize(group->MarkSize());
 
648
      else if (obj->AsGroup())
 
649
        obj->AsGroup()->SetMarkSize(group->MarkSize());
 
650
    }
 
651
    if (group->MarkShape()) {
 
652
      if (obj->AsMark())
 
653
        obj->AsMark()->SetShape(group->MarkShape());
 
654
      else if (obj->AsGroup())
 
655
        obj->AsGroup()->SetMarkShape(group->MarkShape());
 
656
    }
 
657
    push_back(IpePgObject(IpePgObject::ESecondary, layer, obj));
 
658
  }
 
659
  erase(it);
 
660
  iEdited = true;
 
661
  EnsurePrimarySelection();
 
662
  return true;
 
663
}
 
664
 
 
665
//! Move selected objects to front.
 
666
void IpePage::Front()
 
667
{
 
668
  IpePgObjectSeq objs;
 
669
  ExtractSelection(objs);
 
670
  for (IpePgObjectSeq::iterator it = objs.begin();
 
671
       it != objs.end(); ++it) {
 
672
    push_back(*it);
 
673
    iEdited = true;
 
674
  }
 
675
}
 
676
 
 
677
//! Move selected objects to back.
 
678
void IpePage::Back()
 
679
{
 
680
  IpePgObjectSeq objs;
 
681
  ExtractSelection(objs);
 
682
  iterator it1 = begin();
 
683
  if (Layer(0).Name() == "background") {
 
684
    // skip all objects in this layer
 
685
    while (it1 != end() && it1->Layer() == 0)
 
686
      ++it1;
 
687
  }
 
688
  for (IpePgObjectSeq::reverse_iterator it = objs.rbegin();
 
689
       it != objs.rend(); ++it) {
 
690
    insert(it1, *it);
 
691
    --it1;
 
692
    iEdited = true;
 
693
  }
 
694
}
 
695
 
 
696
//! Duplicate the selected objects into \a layer.
 
697
void IpePage::Duplicate(int layer)
 
698
{
 
699
  // remember number of elements on page
 
700
  iterator it = begin();
 
701
  int n = size();
 
702
  while (n-- > 0) {
 
703
    if (it->Select()) {
 
704
      push_back(IpePgObject(IpePgObject::ESecondary, layer,
 
705
                            it->Object()->Clone()));
 
706
      it->SetSelect(IpePgObject::ENone);  // unselect old
 
707
    }
 
708
    ++it;
 
709
    iEdited = true;
 
710
  }
 
711
  EnsurePrimarySelection();
 
712
}
 
713
 
 
714
//! Set stroke color of selected objects.
 
715
void IpePage::SetStroke(IpeAttribute color)
 
716
{
 
717
  for (iterator it = begin(); it != end(); ++it) {
 
718
    if (it->Select() != IpePgObject::ENone) {
 
719
      it->Object()->SetStroke(color);
 
720
      iEdited = true;
 
721
    }
 
722
  }
 
723
}
 
724
 
 
725
//! Set fill color of selected objects.
 
726
void IpePage::SetFill(IpeAttribute color)
 
727
{
 
728
  for (iterator it = begin(); it != end(); ++it) {
 
729
    if (it->Select() != IpePgObject::ENone) {
 
730
      IpeFillable *obj = it->Object()->AsFillable();
 
731
      if (obj) {
 
732
        obj->SetFill(color);
 
733
        iEdited = true;
 
734
      }
 
735
    }
 
736
  }
 
737
}
 
738
 
 
739
//! Set line width of selected objects.
 
740
void IpePage::SetLineWidth(IpeAttribute attr)
 
741
{
 
742
  for (iterator it = begin(); it != end(); ++it) {
 
743
    if (it->Select() != IpePgObject::ENone) {
 
744
      IpeFillable *obj = it->Object()->AsFillable();
 
745
      if (obj) {
 
746
        obj->SetLineWidth(attr);
 
747
        iEdited = true;
 
748
      }
 
749
    }
 
750
  }
 
751
}
 
752
 
 
753
//! Set line style of selected objects.
 
754
void IpePage::SetDashStyle(IpeAttribute attr)
 
755
{
 
756
  for (iterator it = begin(); it != end(); ++it) {
 
757
    if (it->Select() != IpePgObject::ENone) {
 
758
      IpeFillable *obj = it->Object()->AsFillable();
 
759
      if (obj) {
 
760
        obj->SetDashStyle(attr);
 
761
        iEdited = true;
 
762
      }
 
763
    }
 
764
  }
 
765
}
 
766
 
 
767
//! Set arrows of selected objects.
 
768
void IpePage::SetArrows(bool forward, bool backward, IpeAttribute size)
 
769
{
 
770
  for (iterator it = begin(); it != end(); ++it) {
 
771
    if (it->Select() != IpePgObject::ENone) {
 
772
      IpePath *path = it->Object()->AsPath();
 
773
      if (path) {
 
774
        if (forward)
 
775
          path->SetForwardArrow(size);
 
776
        else
 
777
          path->SetForwardArrow(IpeAttribute());
 
778
        if (backward)
 
779
          path->SetBackwardArrow(size);
 
780
        else
 
781
          path->SetBackwardArrow(IpeAttribute());
 
782
        iEdited = true;
 
783
      }
 
784
    }
 
785
  }
 
786
}
 
787
 
 
788
//! Set arrow size of selected objects.
 
789
void IpePage::SetArrowSize(IpeAttribute size)
 
790
{
 
791
  for (iterator it = begin(); it != end(); ++it) {
 
792
    if (it->Select() != IpePgObject::ENone) {
 
793
      IpePath *path = it->Object()->AsPath();
 
794
      if (path) {
 
795
        if (!path->ForwardArrow().IsNull())
 
796
          path->SetForwardArrow(size);
 
797
        if (!path->BackwardArrow().IsNull())
 
798
          path->SetBackwardArrow(size);
 
799
        iEdited = true;
 
800
      }
 
801
    }
 
802
  }
 
803
}
 
804
 
 
805
//! Set text size of selected objects.
 
806
void IpePage::SetTextSize(IpeAttribute size)
 
807
{
 
808
  for (iterator it = begin(); it != end(); ++it) {
 
809
    if (it->Select() != IpePgObject::ENone) {
 
810
      IpeText *obj = it->Object()->AsText();
 
811
      if (obj) {
 
812
        obj->SetSize(size);
 
813
        it->InvalidateBBox();
 
814
        obj->SetXForm(0); // XForm is no longer useful
 
815
        iEdited = true;
 
816
      } else {
 
817
        IpeGroup *grp = it->Object()->AsGroup();
 
818
        if (grp) {
 
819
          grp->SetTextSize(size);
 
820
          iEdited = true;
 
821
        }
 
822
      }
 
823
    }
 
824
  }
 
825
}
 
826
 
 
827
//! Set mark size of selected objects.
 
828
void IpePage::SetMarkSize(IpeAttribute size)
 
829
{
 
830
  for (iterator it = begin(); it != end(); ++it) {
 
831
    if (it->Select() != IpePgObject::ENone) {
 
832
      IpeMark *obj = it->Object()->AsMark();
 
833
      if (obj) {
 
834
        obj->SetSize(size);
 
835
        iEdited = true;
 
836
      } else {
 
837
        IpeGroup *grp = it->Object()->AsGroup();
 
838
        if (grp) {
 
839
          grp->SetMarkSize(size);
 
840
          iEdited = true;
 
841
        }
 
842
      }
 
843
    }
 
844
  }
 
845
}
 
846
 
 
847
//! Set mark shape of selected objects.
 
848
void IpePage::SetMarkShape(int shape)
 
849
{
 
850
  for (iterator it = begin(); it != end(); ++it) {
 
851
    if (it->Select() != IpePgObject::ENone) {
 
852
      IpeMark *obj = it->Object()->AsMark();
 
853
      if (obj) {
 
854
        obj->SetShape(shape);
 
855
        iEdited = true;
 
856
      } else {
 
857
        IpeGroup *grp = it->Object()->AsGroup();
 
858
        if (grp) {
 
859
          grp->SetMarkShape(shape);
 
860
          iEdited = true;
 
861
        }
 
862
      }
 
863
    }
 
864
  }
 
865
}
 
866
 
 
867
//! Copy selected objects into the stream.
 
868
void IpePage::Copy(IpeStream &stream, const IpeStyleSheet *sheet) const
 
869
{
 
870
  IpeBitmapFinder bmFinder;
 
871
  for (const_iterator it = begin(); it != end(); ++it) {
 
872
    if (it->Select() != IpePgObject::ENone)
 
873
      it->Object()->Accept(bmFinder);
 
874
  }
 
875
  stream << "<ipeselection>\n";
 
876
  int id = 1;
 
877
  for (std::vector<IpeBitmap>::const_iterator it = bmFinder.iBitmaps.begin();
 
878
       it != bmFinder.iBitmaps.end(); ++it) {
 
879
    it->SaveAsXml(stream, id);
 
880
    it->SetObjNum(id);
 
881
    ++id;
 
882
  }
 
883
  IpePainter painter(sheet);
 
884
  for (const_iterator it = begin(); it != end(); ++it) {
 
885
    if (it->Select() != IpePgObject::ENone) {
 
886
      it->Object()->SaveAsXml(painter, stream, IpeString());
 
887
    }
 
888
  }
 
889
  stream << "</ipeselection>\n";
 
890
}
 
891
 
 
892
//! Copy whole page into the stream.
 
893
void IpePage::CopyPage(IpeStream &stream, const IpeStyleSheet *sheet) const
 
894
{
 
895
  IpeBitmapFinder bmFinder;
 
896
  bmFinder.ScanPage(this);
 
897
  stream << "<ipepage>\n";
 
898
  int id = 1;
 
899
  for (std::vector<IpeBitmap>::const_iterator it = bmFinder.iBitmaps.begin();
 
900
       it != bmFinder.iBitmaps.end(); ++it) {
 
901
    IpeBitmap bm = *it;
 
902
    bm.SaveAsXml(stream, id);
 
903
    bm.SetObjNum(id);
 
904
    ++id;
 
905
  }
 
906
  IpePainter painter(sheet);
 
907
  SaveAsXml(painter, stream);
 
908
  stream << "</ipepage>\n";
 
909
}
 
910
 
 
911
//! Paste objects from XML source into \a layer.
 
912
/*! Returns false if XML source cannot be parsed. */
 
913
bool IpePage::Paste(int layer, IpeXmlDataSource &source,
 
914
                    IpeRepository *rep)
 
915
{
 
916
  IpeImlParser parser(source, rep);
 
917
  IpePgObjectSeq seq;
 
918
  if (!parser.ParseSelection(seq) || seq.empty())
 
919
    return false;
 
920
 
 
921
  DeselectAll();
 
922
  IpePgObject::TSelect sel = IpePgObject::EPrimary;
 
923
  for (IpePgObjectSeq::const_iterator it = seq.begin();
 
924
       it != seq.end(); ++it) {
 
925
    push_back(IpePgObject(sel, layer, it->Object()->Clone()));
 
926
    sel = IpePgObject::ESecondary;
 
927
  }
 
928
  iEdited = true;
 
929
  return true;
 
930
}
 
931
 
 
932
//! If no selected object is close, select closest object.
 
933
/*! If there is a selected object at distance at most \a d from \a
 
934
  pos, return true.  Otherwise, check whether the closest object to \a
 
935
  pos has distance at most \a d.  If so, unselect everything, make
 
936
  this object the primary selection, and return true.  If not, return
 
937
  whether the page has a selection at all.
 
938
 
 
939
  If \a primaryOnly is \c true, the primary selection has to be at
 
940
  distance at most \a d, otherwise it'll be replaced as above.
 
941
*/
 
942
bool IpePage::UpdateCloseSelection(const IpeVector &pos, double d,
 
943
                                   bool primaryOnly)
 
944
{
 
945
  // check whether a selected object is close enough
 
946
  if (primaryOnly) {
 
947
    iterator it = PrimarySelection();
 
948
    if (it != end() && it->Distance(pos, d) < d)
 
949
      return true;
 
950
  } else {
 
951
    for (iterator it = begin(); it != end(); ++it) {
 
952
      if (it->Select() != IpePgObject::ENone &&
 
953
          it->Distance(pos, d) < d)
 
954
        return true;
 
955
    }
 
956
  }
 
957
 
 
958
  // no --- find closest object
 
959
  double d1;
 
960
  iterator it1 = end();
 
961
  for (iterator it = begin(); it != end(); ++it) {
 
962
    if (Layer(it->Layer()).IsVisible() &&
 
963
        !Layer(it->Layer()).IsLocked()) {
 
964
      if ((d1 = it->Distance(pos, d)) < d) {
 
965
        d = d1;
 
966
        it1 = it;
 
967
      }
 
968
    }
 
969
  }
 
970
 
 
971
  // closest object close enough?
 
972
  if (it1 != end()) {
 
973
    // deselect all, and select it
 
974
    for (iterator it = begin(); it != end(); ++it)
 
975
      it->SetSelect(IpePgObject::ENone);
 
976
    it1->SetSelect(IpePgObject::EPrimary);
 
977
    return true;
 
978
  }
 
979
  return HasSelection();
 
980
}
 
981
 
 
982
// --------------------------------------------------------------------
 
983
 
 
984
//! Create one path object with all the subpaths from the selection.
 
985
/*! The new object takes the attributes from the primary selection,
 
986
  and is placed in \a layer.
 
987
  The function returns false if non-path objects are selected. */
 
988
bool IpePage::ComposePaths(int layer)
 
989
{
 
990
  for (iterator it = begin(); it != end(); ++it) {
 
991
    if (it->Select() && !it->Object()->AsPath())
 
992
      return false;
 
993
  }
 
994
  IpePath *prim = PrimarySelection()->Object()->AsPath();
 
995
  IpeAllAttributes attr;
 
996
  attr.iStroke = prim->Stroke();
 
997
  attr.iFill = prim->Fill();
 
998
  attr.iLineWidth = prim->LineWidth();
 
999
  attr.iDashStyle = prim->DashStyle();
 
1000
  attr.iForwardArrow = false;
 
1001
  attr.iBackwardArrow = false;
 
1002
  IpePath *obj = new IpePath(attr);
 
1003
 
 
1004
  iterator it1;
 
1005
  for (iterator it = begin(); it != end(); it = it1) {
 
1006
    it1 = it;
 
1007
    ++it1;
 
1008
    if (it->Select()) {
 
1009
      IpePath *p = it->Object()->AsPath();
 
1010
      for (int i = 0; i < p->NumSubPaths(); ++i)
 
1011
        obj->AddSubPath(p->SubPath(i)->Transform(p->Matrix()));
 
1012
      erase(it);
 
1013
    }
 
1014
  }
 
1015
  push_back(IpePgObject(IpePgObject::EPrimary, layer, obj));
 
1016
  iEdited = true;
 
1017
  return true;
 
1018
}
 
1019
 
 
1020
//! Decompose one path object into separate objects for the subpaths.
 
1021
/*! The new objects are placed in \a layer.
 
1022
  The function returns false if the primary selection is not a path object. */
 
1023
bool IpePage::DecomposePath(int layer)
 
1024
{
 
1025
  IpePath *prim = PrimarySelection()->Object()->AsPath();
 
1026
  if (!prim)
 
1027
    return false;
 
1028
  IpeAllAttributes attr;
 
1029
  attr.iStroke = prim->Stroke();
 
1030
  attr.iFill = prim->Fill();
 
1031
  attr.iLineWidth = prim->LineWidth();
 
1032
  attr.iDashStyle = prim->DashStyle();
 
1033
  attr.iForwardArrow = false;
 
1034
  attr.iBackwardArrow = false;
 
1035
  IpeMatrix tfm = prim->Matrix();
 
1036
 
 
1037
  for (int i = 0; i < prim->NumSubPaths(); ++i) {
 
1038
    IpeSubPath *sp = prim->SubPath(i)->Clone();
 
1039
    IpePath *obj = new IpePath(attr);
 
1040
    obj->SetMatrix(tfm);
 
1041
    obj->AddSubPath(sp);
 
1042
    push_back(IpePgObject(IpePgObject::ESecondary, layer, obj));
 
1043
  }
 
1044
  erase(PrimarySelection());
 
1045
  EnsurePrimarySelection();
 
1046
  iEdited = true;
 
1047
  return true;
 
1048
}
 
1049
 
 
1050
// --------------------------------------------------------------------
 
1051
 
 
1052
const double joinThreshold = 0.000001;
 
1053
 
 
1054
struct SSubPath {
 
1055
  IpeVector iV[2];
 
1056
  const IpeSegmentSubPath *iPath;  // not owned
 
1057
  bool iFlip;
 
1058
};
 
1059
 
 
1060
static int FindPartner(const IpeVector &v, SSubPath *subs, int beg, int end)
 
1061
{
 
1062
  while (beg != end) {
 
1063
    if ((v - subs[beg].iV[0]).SqLen() < joinThreshold)
 
1064
      return beg;
 
1065
    if ((v - subs[beg].iV[1]).SqLen() < joinThreshold) {
 
1066
      IpeVector t = subs[beg].iV[0];
 
1067
      subs[beg].iV[0] = subs[beg].iV[1];
 
1068
      subs[beg].iV[1] = t;
 
1069
      subs[beg].iFlip = true;
 
1070
      return beg;
 
1071
    }
 
1072
    ++beg;
 
1073
  }
 
1074
  return -1;
 
1075
}
 
1076
 
 
1077
IpePath *IpePage::DoJoinPaths(IpePath *prim, SSubPath *subs, int size)
 
1078
{
 
1079
  bool closePath =
 
1080
    ((subs[0].iV[0] - subs[size-1].iV[1]).SqLen() < joinThreshold);
 
1081
 
 
1082
  IpeAllAttributes attr;
 
1083
  attr.iStroke = prim->Stroke();
 
1084
  attr.iFill = prim->Fill();
 
1085
  attr.iLineWidth = prim->LineWidth();
 
1086
  attr.iDashStyle = prim->DashStyle();
 
1087
  attr.iForwardArrow = false;
 
1088
  attr.iBackwardArrow = false;
 
1089
 
 
1090
  IpeSegmentSubPath *sp = new IpeSegmentSubPath;
 
1091
 
 
1092
  for (int i = 0; i < size; ++i) {
 
1093
    const IpeSegmentSubPath *p = subs[i].iPath;
 
1094
    if (subs[i].iFlip) {
 
1095
      for (int j = p->NumSegments() - 1; j >= 0; --j)
 
1096
        sp->AppendReversed(p->Segment(j));
 
1097
    } else {
 
1098
      for (int j = 0; j < p->NumSegments(); ++j)
 
1099
        sp->Append(p->Segment(j));
 
1100
    }
 
1101
  }
 
1102
 
 
1103
  IpePath *obj = new IpePath(attr);
 
1104
  sp->SetClosed(closePath);
 
1105
  obj->AddSubPath(sp);
 
1106
  return obj;
 
1107
}
 
1108
 
 
1109
//! Join paths into one long path.
 
1110
/*! Create one path object with the open subpaths from the selection
 
1111
  joined into one long subpath.
 
1112
  The new object takes the attributes from the primary selection,
 
1113
  and is placed in \a layer.
 
1114
  The function returns \c false if objects are selected that do not consist
 
1115
  of open subpaths only.
 
1116
*/
 
1117
bool IpePage::JoinPaths(int layer)
 
1118
{
 
1119
  std::vector<SSubPath> subs;
 
1120
  for (iterator it = begin(); it != end(); ++it) {
 
1121
    if (it->Select()) {
 
1122
      IpePath *p = it->Object()->AsPath();
 
1123
      // must be a single open subpath
 
1124
      if (!p || p->NumSubPaths() > 1 || p->SubPath(0)->Closed())
 
1125
        return false;
 
1126
      const IpeSegmentSubPath *sp = p->SubPath(0)->AsSegs();
 
1127
      SSubPath sub;
 
1128
      sub.iPath = sp;
 
1129
      sub.iV[0] = sp->Segment(0).CP(0);
 
1130
      IpePathSegment back = sp->Segment(-1);
 
1131
      sub.iV[1] = back.CP(back.NumCP() - 1);
 
1132
      sub.iFlip = false;
 
1133
      subs.push_back(sub);
 
1134
    }
 
1135
  }
 
1136
  // match up endpoints
 
1137
  if (FindPartner(subs[0].iV[1], &subs[0], 1, subs.size()) < 0) {
 
1138
    // not found, flip subs[0] and try again
 
1139
    IpeVector t = subs[0].iV[0];
 
1140
    subs[0].iV[0] = subs[0].iV[1];
 
1141
    subs[0].iV[1] = t;
 
1142
    subs[0].iFlip = true;
 
1143
  }
 
1144
  for (int i = 0; i < int(subs.size()) - 2; ++i) {
 
1145
    // invariant: subs[0] .. subs[i] form a chain
 
1146
    int j = FindPartner(subs[i].iV[1], &subs[0], i+1, subs.size());
 
1147
    if (j < 0)
 
1148
      return false; // no match
 
1149
    // flip i+1 and j
 
1150
    if (j != i+1) {
 
1151
      SSubPath sub = subs[i+1];
 
1152
      subs[i+1] = subs[j];
 
1153
      subs[j] = sub;
 
1154
    }
 
1155
  }
 
1156
  IpePath *prim = PrimarySelection()->Object()->AsPath();
 
1157
  IpePath *obj = DoJoinPaths(prim, &subs[0], subs.size());
 
1158
  // erase the selected objects
 
1159
  Delete();
 
1160
  push_back(IpePgObject(IpePgObject::EPrimary, layer, obj));
 
1161
  iEdited = true;
 
1162
  return true;
 
1163
}
 
1164
 
 
1165
// --------------------------------------------------------------------