]> git.lyx.org Git - lyx.git/blob - src/BufferView_pimpl.C
Rename screen, and don't re-construct it on a buffer change (that is far too
[lyx.git] / src / BufferView_pimpl.C
1 #include <config.h>
2
3 #ifdef __GNUG__
4 #pragma implementation
5 #endif
6
7 #include "BufferView_pimpl.h"
8 #include "frontends/WorkArea.h"
9 #include "frontends/screen.h"
10 #include "frontends/Dialogs.h"
11 #include "frontends/Alert.h"
12 #include "frontends/FileDialog.h"
13 #include "lyxtext.h"
14 #include "lyxrow.h"
15 #include "paragraph.h"
16 #include "frontends/LyXView.h"
17 #include "commandtags.h"
18 #include "lyxfunc.h"
19 #include "debug.h"
20 #include "bufferview_funcs.h"
21 #include "TextCache.h"
22 #include "bufferlist.h"
23 #include "lyxrc.h"
24 #include "intl.h"
25 // added for Dispatch functions
26 #include "lyx_cb.h"
27 #include "lyx_main.h"
28 #include "FloatList.h"
29 #include "gettext.h"
30 #include "ParagraphParameters.h"
31 #include "undo_funcs.h"
32 #include "lyxtextclasslist.h"
33
34
35 #include "insets/insetbib.h"
36 #include "insets/insettext.h"
37 #include "insets/inseturl.h"
38 #include "insets/insetlatexaccent.h"
39 #include "insets/insettoc.h"
40 #include "insets/insetref.h"
41 #include "insets/insetparent.h"
42 #include "insets/insetindex.h"
43 #include "insets/insetnote.h"
44 #include "insets/insetinclude.h"
45 #include "insets/insetcite.h"
46 #include "insets/insetert.h"
47 #include "insets/insetexternal.h"
48 #include "insets/insetgraphics.h"
49 #include "insets/insetfoot.h"
50 #include "insets/insetmarginal.h"
51 #include "insets/insetminipage.h"
52 #include "insets/insetfloat.h"
53 #include "insets/insettabular.h"
54 #if 0
55 #include "insets/insettheorem.h"
56 #include "insets/insetlist.h"
57 #endif
58 #include "insets/insetcaption.h"
59 #include "insets/insetfloatlist.h"
60 #include "insets/insetspecialchar.h"
61
62 #include "mathed/formulabase.h"
63
64 #include "support/LAssert.h"
65 #include "support/lstrings.h"
66 #include "support/filetools.h"
67 #include "support/lyxfunctional.h"
68
69 #include <boost/bind.hpp>
70
71 #include <ctime>
72 #include <unistd.h>
73 #include <sys/wait.h>
74 #include <clocale>
75
76
77 extern string current_layout;
78
79 #ifndef CXX_GLOBAL_CSTD
80 using std::tm;
81 using std::localtime;
82 using std::time;
83 using std::setlocale;
84 using std::strftime;
85 #endif
86
87 using std::vector;
88 using std::find_if;
89 using std::find;
90 using std::pair;
91 using std::endl;
92 using std::make_pair;
93 using std::min;
94
95 using lyx::pos_type;
96 using lyx::textclass_type;
97
98 /* the selection possible is needed, that only motion events are
99  * used, where the bottom press event was on the drawing area too */
100 bool selection_possible = false;
101
102 extern BufferList bufferlist;
103 extern char ascii_type;
104
105 extern int bibitemMaxWidth(BufferView *, LyXFont const &);
106
107
108 namespace {
109
110 const unsigned int saved_positions_num = 20;
111
112 inline
113 void waitForX()
114 {
115         XSync(fl_get_display(), 0);
116 }
117
118
119 void SetXtermCursor(Window win)
120 {
121         static Cursor cursor;
122         static bool cursor_undefined = true;
123         if (cursor_undefined) {
124                 cursor = XCreateFontCursor(fl_get_display(), XC_xterm);
125                 XFlush(fl_get_display());
126                 cursor_undefined = false;
127         }
128         XDefineCursor(fl_get_display(), win, cursor);
129         XFlush(fl_get_display());
130 }
131
132 } // anon namespace
133
134
135 BufferView::Pimpl::Pimpl(BufferView * b, LyXView * o,
136              int xpos, int ypos, int width, int height)
137         : bv_(b), owner_(o), buffer_(0),
138           current_scrollbar_value(0), cursor_timeout(400),
139           using_xterm_cursor(false), inset_slept(false)
140 {
141         workarea_.reset(new WorkArea(xpos, ypos, width, height));
142         screen_.reset(new LScreen(workarea()));
143  
144         // Setup the signals
145         workarea().scrollCB.connect(boost::bind(&BufferView::Pimpl::scrollCB, this, _1));
146         workarea().workAreaExpose
147                 .connect(boost::bind(&BufferView::Pimpl::workAreaExpose, this));
148         workarea().workAreaEnter
149                 .connect(boost::bind(&BufferView::Pimpl::enterView, this));
150         workarea().workAreaLeave
151                 .connect(boost::bind(&BufferView::Pimpl::leaveView, this));
152         workarea().workAreaButtonPress
153                 .connect(boost::bind(&BufferView::Pimpl::workAreaButtonPress, this, _1, _2, _3));
154         workarea().workAreaButtonRelease
155                 .connect(boost::bind(&BufferView::Pimpl::workAreaButtonRelease, this, _1, _2, _3));
156         workarea().workAreaMotionNotify
157                 .connect(boost::bind(&BufferView::Pimpl::workAreaMotionNotify, this, _1, _2, _3));
158         workarea().workAreaDoubleClick
159                 .connect(boost::bind(&BufferView::Pimpl::doubleClick, this, _1, _2, _3));
160         workarea().workAreaTripleClick
161                 .connect(boost::bind(&BufferView::Pimpl::tripleClick, this, _1, _2, _3));
162         workarea().workAreaKeyPress
163                 .connect(boost::bind(&BufferView::Pimpl::workAreaKeyPress, this, _1, _2));
164         workarea().selectionRequested
165                 .connect(boost::bind(&BufferView::Pimpl::selectionRequested, this));
166         workarea().selectionLost
167                 .connect(boost::bind(&BufferView::Pimpl::selectionLost, this));
168
169         cursor_timeout.timeout.connect(boost::bind(&BufferView::Pimpl::cursorToggle, this));
170         cursor_timeout.start();
171         workarea().setFocus();
172         saved_positions.resize(saved_positions_num);
173 }
174
175
176 WorkArea & BufferView::Pimpl::workarea() const
177 {
178         return *workarea_.get();
179 }
180
181  
182 LScreen & BufferView::Pimpl::screen() const
183 {
184         return *screen_.get();
185 }
186
187
188 Painter & BufferView::Pimpl::painter()
189 {
190         return workarea().getPainter();
191 }
192
193
194 void BufferView::Pimpl::buffer(Buffer * b)
195 {
196         lyxerr[Debug::INFO] << "Setting buffer in BufferView ("
197                             << b << ")" << endl;
198         if (buffer_) {
199 #if 0
200                 insetSleep();
201 #endif
202                 buffer_->delUser(bv_);
203
204                 // Put the old text into the TextCache, but
205                 // only if the buffer is still loaded.
206                 // Also set the owner of the test to 0
207                 //              bv_->text->owner(0);
208                 textcache.add(buffer_, workarea().workWidth(), bv_->text);
209                 if (lyxerr.debugging())
210                         textcache.show(lyxerr, "BufferView::buffer");
211
212                 bv_->text = 0;
213         }
214
215         // Set current buffer
216         buffer_ = b;
217
218         if (bufferlist.getState() == BufferList::CLOSING) return;
219
220         // If we are closing the buffer, use the first buffer as current
221         if (!buffer_) {
222                 buffer_ = bufferlist.first();
223         }
224
225         if (buffer_) {
226                 lyxerr[Debug::INFO] << "Buffer addr: " << buffer_ << endl;
227                 buffer_->addUser(bv_);
228                 // If we don't have a text object for this, we make one
229                 if (bv_->text == 0) {
230                         resizeCurrentBuffer();
231                 } else {
232                         updateScreen();
233                         updateScrollbar();
234                 }
235                 bv_->text->first_y = screen().topCursorVisible(bv_->text);
236                 owner_->updateMenubar();
237                 owner_->updateToolbar();
238                 // Similarly, buffer-dependent dialogs should be updated or
239                 // hidden. This should go here because some dialogs (eg ToC)
240                 // require bv_->text.
241                 owner_->getDialogs()->updateBufferDependent(true);
242                 redraw();
243 #if 0
244                 insetWakeup();
245 #endif
246         } else {
247                 lyxerr[Debug::INFO] << "  No Buffer!" << endl;
248                 owner_->updateMenubar();
249                 owner_->updateToolbar();
250                 owner_->getDialogs()->hideBufferDependent();
251                 updateScrollbar();
252                 workarea().redraw();
253
254                 // Also remove all remaining text's from the testcache.
255                 // (there should not be any!) (if there is any it is a
256                 // bug!)
257                 if (lyxerr.debugging())
258                         textcache.show(lyxerr, "buffer delete all");
259                 textcache.clear();
260         }
261         // should update layoutchoice even if we don't have a buffer.
262         owner_->updateLayoutChoice();
263
264         owner_->updateWindowTitle();
265 }
266
267
268 void BufferView::Pimpl::resize(int xpos, int ypos, int width, int height)
269 {
270         workarea().resize(xpos, ypos, width, height);
271         update(bv_->text, SELECT);
272         redraw();
273 }
274
275
276 void BufferView::Pimpl::resize()
277 {
278         if (buffer_)
279                 resizeCurrentBuffer();
280 }
281
282
283 void BufferView::Pimpl::redraw()
284 {
285         lyxerr[Debug::INFO] << "BufferView::redraw()" << endl;
286         workarea().redraw();
287 }
288
289
290 bool BufferView::Pimpl::fitCursor()
291 {
292         bool ret;
293
294         if (bv_->theLockingInset()) {
295                 bv_->theLockingInset()->fitInsetCursor(bv_);
296                 ret = true;
297         } else {
298                 ret = screen().fitCursor(bv_->text, bv_);
299         }
300
301         bv_->owner()->getDialogs()->updateParagraph();
302         if (ret)
303             updateScrollbar();
304         return ret;
305 }
306
307
308 void BufferView::Pimpl::redoCurrentBuffer()
309 {
310         lyxerr[Debug::INFO] << "BufferView::redoCurrentBuffer" << endl;
311         if (buffer_ && bv_->text) {
312                 resize();
313                 owner_->updateLayoutChoice();
314         }
315 }
316
317
318 int BufferView::Pimpl::resizeCurrentBuffer()
319 {
320         lyxerr[Debug::INFO] << "resizeCurrentBuffer" << endl;
321
322         Paragraph * par = 0;
323         Paragraph * selstartpar = 0;
324         Paragraph * selendpar = 0;
325         UpdatableInset * the_locking_inset = 0;
326
327         pos_type pos = 0;
328         pos_type selstartpos = 0;
329         pos_type selendpos = 0;
330         bool selection = false;
331         bool mark_set  = false;
332
333         owner_->prohibitInput();
334
335         owner_->message(_("Formatting document..."));
336
337         if (bv_->text) {
338                 par = bv_->text->cursor.par();
339                 pos = bv_->text->cursor.pos();
340                 selstartpar = bv_->text->selection.start.par();
341                 selstartpos = bv_->text->selection.start.pos();
342                 selendpar = bv_->text->selection.end.par();
343                 selendpos = bv_->text->selection.end.pos();
344                 selection = bv_->text->selection.set();
345                 mark_set = bv_->text->selection.mark();
346                 the_locking_inset = bv_->theLockingInset();
347                 buffer_->resizeInsets(bv_);
348                 // I don't think the delete and new are necessary here we just could
349                 // call only init! (Jug 20020419)
350                 delete bv_->text;
351                 bv_->text = new LyXText(bv_);
352                 bv_->text->init(bv_);
353         } else {
354                 // See if we have a text in TextCache that fits
355                 // the new buffer_ with the correct width.
356                 bv_->text = textcache.findFit(buffer_, workarea().workWidth());
357                 if (bv_->text) {
358                         if (lyxerr.debugging()) {
359                                 lyxerr << "Found a LyXText that fits:\n";
360                                 textcache.show(lyxerr, make_pair(buffer_, make_pair(workarea().workWidth(), bv_->text)));
361                         }
362                         // Set the owner of the newly found text
363                         //      bv_->text->owner(bv_);
364                         if (lyxerr.debugging())
365                                 textcache.show(lyxerr, "resizeCurrentBuffer");
366                 } else {
367                         bv_->text = new LyXText(bv_);
368                         bv_->text->init(bv_);
369                         //buffer_->resizeInsets(bv_);
370                 }
371         }
372
373         updateScreen();
374
375         if (par) {
376                 bv_->text->selection.set(true);
377                 // At this point just to avoid the Delete-Empty-Paragraph-
378                 // Mechanism when setting the cursor.
379                 bv_->text->selection.mark(mark_set);
380                 if (selection) {
381                         bv_->text->setCursor(bv_, selstartpar, selstartpos);
382                         bv_->text->selection.cursor = bv_->text->cursor;
383                         bv_->text->setCursor(bv_, selendpar, selendpos);
384                         bv_->text->setSelection(bv_);
385                         bv_->text->setCursor(bv_, par, pos);
386                 } else {
387                         bv_->text->setCursor(bv_, par, pos);
388                         bv_->text->selection.cursor = bv_->text->cursor;
389                         bv_->text->selection.set(false);
390                 }
391                 // remake the inset locking
392                 bv_->theLockingInset(the_locking_inset);
393         }
394
395         bv_->text->first_y = screen().topCursorVisible(bv_->text);
396
397         // this will scroll the screen such that the cursor becomes visible
398         updateScrollbar();
399         redraw();
400
401         setState();
402         owner_->allowInput();
403
404         /// clear the "Formatting Document" message
405         owner_->message("");
406
407         return 0;
408 }
409
410
411 void BufferView::Pimpl::updateScreen()
412 {
413         // Regenerate the screen.
414         screen().reset();
415 }
416
417
418 void BufferView::Pimpl::updateScrollbar()
419 {
420         if (!bv_->text) {
421                 lyxerr[Debug::GUI] << "no text in updateScrollbar" << endl;
422                 workarea().setScrollbar(0, 1.0);
423                 return;
424         }
425
426         long const text_height = bv_->text->height;
427         long const work_height = workarea().height();
428
429         double const lineh = bv_->text->defaultHeight();
430         double const slider_size =
431                 (text_height == 0) ? 1.0 : 1.0 / double(text_height);
432
433         lyxerr[Debug::GUI] << "text_height now " << text_height << endl;
434         lyxerr[Debug::GUI] << "work_height " << work_height << endl;
435
436         /* If the text is smaller than the working area, the scrollbar
437          * maximum must be the working area height. No scrolling will
438          * be possible */
439         if (text_height <= work_height) {
440                 lyxerr[Debug::GUI] << "doc smaller than workarea !" << endl;
441                 workarea().setScrollbarBounds(0.0, 0.0);
442                 current_scrollbar_value = bv_->text->first_y;
443                 workarea().setScrollbar(current_scrollbar_value, 1.0);
444                 return;
445         }
446
447         workarea().setScrollbarBounds(0.0, text_height - work_height);
448         workarea().setScrollbarIncrements(lineh);
449         current_scrollbar_value = bv_->text->first_y;
450         workarea().setScrollbar(current_scrollbar_value, slider_size);
451 }
452
453
454 // Callback for scrollbar slider
455 void BufferView::Pimpl::scrollCB(double value)
456 {
457         lyxerr[Debug::GUI] << "scrollCB of " << value << endl;
458
459         if (!buffer_) return;
460
461         current_scrollbar_value = long(value);
462
463         if (current_scrollbar_value < 0)
464                 current_scrollbar_value = 0;
465
466         screen().draw(bv_->text, bv_, current_scrollbar_value);
467
468         if (!lyxrc.cursor_follows_scrollbar) {
469                 waitForX();
470                 return;
471         }
472
473         LyXText * vbt = bv_->text;
474
475         int const height = vbt->defaultHeight();
476         int const first = static_cast<int>((bv_->text->first_y + height));
477         int const last = static_cast<int>((bv_->text->first_y + workarea().height() - height));
478
479         if (vbt->cursor.y() < first)
480                 vbt->setCursorFromCoordinates(bv_, 0, first);
481         else if (vbt->cursor.y() > last)
482                 vbt->setCursorFromCoordinates(bv_, 0, last);
483
484         waitForX();
485 }
486
487
488 int BufferView::Pimpl::scrollUp(long time)
489 {
490         if (!buffer_)
491                 return 0;
492
493         double value = workarea().getScrollbarValue();
494
495         if (value == 0)
496                 return 0;
497
498 #if 1
499         float add_value =  (bv_->text->defaultHeight()
500                             + float(time) * float(time) * 0.125);
501
502         if (add_value > workarea().height())
503                 add_value = float(workarea().height() -
504                                   bv_->text->defaultHeight());
505 #else
506         float add_value =  float(workarea().height()) * float(time) / 100;
507 #endif
508
509         value -= add_value;
510
511         if (value < 0)
512                 value = 0;
513
514         workarea().setScrollbarValue(value);
515
516         scrollCB(value);
517         return 0;
518 }
519
520
521 int BufferView::Pimpl::scrollDown(long time)
522 {
523         if (!buffer_)
524                 return 0;
525
526         double value = workarea().getScrollbarValue();
527         pair<float, float> p = workarea().getScrollbarBounds();
528         double const max = p.second;
529
530         if (value == max)
531                 return 0;
532
533 #if 1
534         float add_value =  (bv_->text->defaultHeight()
535                             + float(time) * float(time) * 0.125);
536
537         if (add_value > workarea().height())
538                 add_value = float(workarea().height() -
539                                   bv_->text->defaultHeight());
540 #else
541         float add_value =  float(workarea().height()) * float(time) / 100;
542 #endif
543
544         value += add_value;
545
546         if (value > max)
547                 value = max;
548
549         workarea().setScrollbarValue(value);
550
551         scrollCB(value);
552         return 0;
553 }
554
555
556 void BufferView::Pimpl::workAreaKeyPress(KeySym keysym, key_modifier::state state)
557 {
558         bv_->owner()->getLyXFunc()->processKeySym(keysym, state);
559 }
560
561
562 void BufferView::Pimpl::workAreaMotionNotify(int x, int y, mouse_button::state state)
563 {
564         // Only use motion with button 1
565         if (!(state & mouse_button::button1))
566                 return;
567
568         if (!buffer_)
569                 return;
570
571         // Check for inset locking
572         if (bv_->theLockingInset()) {
573                 LyXCursor cursor = bv_->text->cursor;
574                 LyXFont font = bv_->text->getFont(buffer_,
575                                                   cursor.par(), cursor.pos());
576                 int width = bv_->theLockingInset()->width(bv_, font);
577                 int inset_x = font.isVisibleRightToLeft()
578                         ? cursor.ix() - width : cursor.ix();
579                 int start_x = inset_x + bv_->theLockingInset()->scroll();
580                 bv_->theLockingInset()->
581                         insetMotionNotify(bv_,
582                                           x - start_x,
583                                           y - cursor.iy() + bv_->text->first_y,
584                                           state);
585                 return;
586         }
587
588         /* The test for not selection possible is needed, that only motion
589            events are used, where the bottom press event was on
590            the drawing area too */
591         if (!selection_possible)
592                 return;
593
594         screen().hideCursor();
595 #if 0
596         int y_before = bv_->text->cursor.y();
597 #endif
598         Row * cursorrow = bv_->text->cursor.row();
599         bv_->text->setCursorFromCoordinates(bv_, x, y + bv_->text->first_y);
600 #if 0
601         // sorry for this but I have a strange error that the y value jumps at
602         // a certain point. This seems like an error in my xforms library or
603         // in some other local environment, but I would like to leave this here
604         // for the moment until I can remove this (Jug 20020418)
605         if (y_before < bv_->text->cursor.y())
606                 lyxerr << y_before << ":" << bv_->text->cursor.y() << endl;
607 #endif
608         // This is to allow jumping over large insets
609         if (cursorrow == bv_->text->cursor.row()) {
610                 if (y >= int(workarea().height())) {
611                         bv_->text->cursorDown(bv_, false);
612                 } else if (y < 0) {
613                         bv_->text->cursorUp(bv_, false);
614                 }
615         }
616
617         if (!bv_->text->selection.set())
618                 update(bv_->text, BufferView::UPDATE); // Maybe an empty line was deleted
619
620         bv_->text->setSelection(bv_);
621         screen().toggleToggle(bv_->text, bv_);
622         fitCursor();
623         showCursor();
624 }
625
626
627 // Single-click on work area
628 void BufferView::Pimpl::workAreaButtonPress(int xpos, int ypos,
629                                             mouse_button::state button)
630 {
631         if (!buffer_)
632                 return;
633
634         // ok ok, this is a hack.
635
636         if (button == mouse_button::button4) {
637                 scrollUp(lyxrc.wheel_jump);
638                 // We shouldn't go further down as we really should only do the
639                 // scrolling and be done with this. Otherwise we may open some
640                 // dialogs (Jug 20020424).
641                 return;
642         } else if (button == mouse_button::button5) {
643                 scrollDown(lyxrc.wheel_jump);
644                 // We shouldn't go further down as we really should only do the
645                 // scrolling and be done with this. Otherwise we may open some
646                 // dialogs (Jug 20020424).
647                 return;
648         }
649
650         Inset * inset_hit = checkInsetHit(bv_->text, xpos, ypos);
651
652         // Middle button press pastes if we have a selection
653         // We do this here as if the selection was inside an inset
654         // it could get cleared on the unlocking of the inset so
655         // we have to check this first
656         bool paste_internally = false;
657         if (button == mouse_button::button2 && bv_->getLyXText()->selection.set()) {
658                 owner_->getLyXFunc()->dispatch(LFUN_COPY);
659                 paste_internally = true;
660         }
661
662         int const screen_first = bv_->text->first_y;
663
664         if (bv_->theLockingInset()) {
665                 // We are in inset locking mode
666
667                 /* Check whether the inset was hit. If not reset mode,
668                    otherwise give the event to the inset */
669                 if (inset_hit == bv_->theLockingInset()) {
670                         bv_->theLockingInset()->
671                                 insetButtonPress(bv_,xpos, ypos,button);
672                         return;
673                 } else {
674                         bv_->unlockInset(bv_->theLockingInset());
675                 }
676         }
677
678         if (!inset_hit)
679                 selection_possible = true;
680         screen().hideCursor();
681
682         // Clear the selection
683         screen().toggleSelection(bv_->text, bv_);
684         bv_->text->clearSelection();
685         bv_->text->fullRebreak(bv_);
686         update();
687         updateScrollbar();
688
689         // Single left click in math inset?
690         if (isHighlyEditableInset(inset_hit)) {
691                 // Highly editable inset, like math
692                 UpdatableInset * inset = static_cast<UpdatableInset *>(inset_hit);
693                 selection_possible = false;
694                 owner_->updateLayoutChoice();
695                 owner_->message(inset->editMessage());
696                 //inset->edit(bv_, xpos, ypos, button);
697                 // We just have to lock the inset before calling a PressEvent on it!
698                 // we don't need the edit() call here! (Jug20020329)
699                 if (!bv_->lockInset(inset)) {
700                         lyxerr[Debug::INSETS] << "Cannot lock inset" << endl;
701                 }
702                 inset->insetButtonPress(bv_, xpos, ypos, button);
703                 return;
704         }
705         // I'm not sure we should continue here if we hit an inset (Jug20020403)
706
707         // Right click on a footnote flag opens float menu
708         if (button == mouse_button::button3) {
709                 selection_possible = false;
710                 return;
711         }
712
713         if (!inset_hit) // otherwise it was already set in checkInsetHit(...)
714                 bv_->text->setCursorFromCoordinates(bv_, xpos, ypos + screen_first);
715         finishUndo();
716         bv_->text->selection.cursor = bv_->text->cursor;
717         bv_->text->cursor.x_fix(bv_->text->cursor.x());
718
719         owner_->updateLayoutChoice();
720         if (fitCursor()) {
721                 selection_possible = false;
722         }
723
724         // Insert primary selection with middle mouse
725         // if there is a local selection in the current buffer,
726         // insert this
727         if (button == mouse_button::button2) {
728                 if (paste_internally)
729                         owner_->getLyXFunc()->dispatch(LFUN_PASTE);
730                 else
731                         owner_->getLyXFunc()->dispatch(LFUN_PASTESELECTION,
732                                                        "paragraph");
733                 selection_possible = false;
734                 return;
735         }
736 }
737
738
739 void BufferView::Pimpl::doubleClick(int /*x*/, int /*y*/, mouse_button::state button)
740 {
741         if (!buffer_)
742                 return;
743
744         LyXText * text = bv_->getLyXText();
745
746         if (text->bv_owner && bv_->theLockingInset())
747                 return;
748
749         if (button == mouse_button::button1) {
750                 if (text->bv_owner) {
751                         screen().hideCursor();
752                         screen().toggleSelection(text, bv_);
753                         text->selectWord(bv_, LyXText::WHOLE_WORD_STRICT);
754                         screen().toggleSelection(text, bv_, false);
755                 } else {
756                         text->selectWord(bv_, LyXText::WHOLE_WORD_STRICT);
757                 }
758                 /* This will fit the cursor on the screen
759                  * if necessary */
760                 update(text, BufferView::SELECT|BufferView::FITCUR);
761         }
762 }
763
764
765 void BufferView::Pimpl::tripleClick(int /*x*/, int /*y*/, mouse_button::state button)
766 {
767         if (!buffer_)
768                 return;
769
770         LyXText * text = bv_->getLyXText();
771
772         if (text->bv_owner && bv_->theLockingInset())
773             return;
774
775         if (button == mouse_button::button1) {
776                 if (text->bv_owner) {
777                         screen().hideCursor();
778                         screen().toggleSelection(text, bv_);
779                 }
780                 text->cursorHome(bv_);
781                 text->selection.cursor = text->cursor;
782                 text->cursorEnd(bv_);
783                 text->setSelection(bv_);
784                 if (text->bv_owner) {
785                         screen().toggleSelection(text, bv_, false);
786                 }
787                 /* This will fit the cursor on the screen
788                  * if necessary */
789                 update(text, BufferView::SELECT|BufferView::FITCUR);
790         }
791 }
792
793
794 void BufferView::Pimpl::selectionRequested()
795 {
796         static string sel;
797
798         if (!available())
799                 return;
800
801         LyXText * text = bv_->getLyXText();
802
803         if (text->selection.set() &&
804                 (!bv_->text->xsel_cache.set() ||
805                  text->selection.start != bv_->text->xsel_cache.start ||
806                  text->selection.end != bv_->text->xsel_cache.end))
807         {
808                 bv_->text->xsel_cache = text->selection;
809                 sel = text->selectionAsString(bv_->buffer(), false);
810         } else if (!text->selection.set()) {
811                 sel = string();
812                 bv_->text->xsel_cache.set(false);
813         }
814         if (!sel.empty()) {
815                 workarea().putClipboard(sel);
816         }
817 }
818
819
820 void BufferView::Pimpl::selectionLost()
821 {
822         if (available()) {
823                 hideCursor();
824                 toggleSelection();
825                 bv_->getLyXText()->clearSelection();
826                 showCursor();
827                 bv_->text->xsel_cache.set(false);
828         }
829 }
830
831
832 void BufferView::Pimpl::enterView()
833 {
834         if (available()) {
835                 SetXtermCursor(workarea().getWin());
836                 using_xterm_cursor = true;
837         }
838 }
839
840
841 void BufferView::Pimpl::leaveView()
842 {
843         if (using_xterm_cursor) {
844                 XUndefineCursor(fl_get_display(), workarea().getWin());
845                 using_xterm_cursor = false;
846         }
847 }
848
849
850 void BufferView::Pimpl::workAreaButtonRelease(int x, int y,
851                                               mouse_button::state button)
852 {
853         // do nothing if we used the mouse wheel
854         if (!buffer_ || button == mouse_button::button4 || button == mouse_button::button5)
855                 return;
856
857         // If we hit an inset, we have the inset coordinates in these
858         // and inset_hit points to the inset.  If we do not hit an
859         // inset, inset_hit is 0, and inset_x == x, inset_y == y.
860         Inset * inset_hit = checkInsetHit(bv_->text, x, y);
861
862         if (bv_->theLockingInset()) {
863                 // We are in inset locking mode.
864
865                 /* LyX does a kind of work-area grabbing for insets.
866                    Only a ButtonPress Event outside the inset will
867                    force a insetUnlock. */
868                 bv_->theLockingInset()->
869                         insetButtonRelease(bv_, x, y, button);
870                 return;
871         }
872
873         selection_possible = false;
874
875         if (button == mouse_button::button2)
876                 return;
877
878         // finish selection
879         if (button == mouse_button::button1) {
880                 workarea().haveSelection(bv_->getLyXText()->selection.set());
881         }
882
883         setState();
884         owner_->showState();
885         owner_->updateMenubar();
886         owner_->updateToolbar();
887
888         // Did we hit an editable inset?
889         if (inset_hit) {
890                 selection_possible = false;
891
892                 // if we reach this point with a selection, it
893                 // must mean we are currently selecting.
894                 // But we don't want to open the inset
895                 // because that is annoying for the user.
896                 // So just pretend we didn't hit it.
897                 // this is OK because a "kosher" ButtonRelease
898                 // will follow a ButtonPress that clears
899                 // the selection.
900                 // Note this also fixes selection drawing
901                 // problems if we end up opening an inset
902                 if (bv_->getLyXText()->selection.set())
903                         return;
904
905                 // CHECK fix this proper in 0.13
906                 // well, maybe 13.0 !!!!!!!!!
907
908                 // Following a ref shouldn't issue
909                 // a push on the undo-stack
910                 // anylonger, now that we have
911                 // keybindings for following
912                 // references and returning from
913                 // references.  IMHO though, it
914                 // should be the inset's own business
915                 // to push or not push on the undo
916                 // stack. They don't *have* to
917                 // alter the document...
918                 // (Joacim)
919                 // ...or maybe the SetCursorParUndo()
920                 // below isn't necessary at all anylonger?
921                 if (inset_hit->lyxCode() == Inset::REF_CODE) {
922                         setCursorParUndo(bv_);
923                 }
924
925                 owner_->message(inset_hit->editMessage());
926
927                 if (isHighlyEditableInset(inset_hit)) {
928                         // Highly editable inset, like math
929                         UpdatableInset *inset = (UpdatableInset *)inset_hit;
930                         inset->insetButtonRelease(bv_, x, y, button);
931                 } else {
932                         inset_hit->insetButtonRelease(bv_, x, y, button);
933                         // IMO this is a grosshack! Inset's should be changed so that
934                         // they call the actions they have to do with the insetButtonRel.
935                         // function and not in the edit(). This should be changed
936                         // (Jug 20020329)
937                         inset_hit->edit(bv_, x, y, button);
938                 }
939                 return;
940         }
941
942         // Maybe we want to edit a bibitem ale970302
943         if (bv_->text->cursor.par()->bibkey && x < 20 +
944             bibitemMaxWidth(bv_, textclasslist[buffer_->params.textclass].defaultfont())) {
945                 bv_->text->cursor.par()->bibkey->edit(bv_, 0, 0, mouse_button::none);
946         }
947
948         return;
949 }
950
951
952 Box BufferView::Pimpl::insetDimensions(LyXText const & text,
953                                        LyXCursor const & cursor) const
954 {
955         Paragraph /*const*/ & par = *cursor.par();
956         pos_type const pos = cursor.pos();
957
958         lyx::Assert(par.getInset(pos));
959
960         Inset const & inset(*par.getInset(pos));
961
962         LyXFont const & font = text.getFont(buffer_, &par, pos);
963
964         int const width = inset.width(bv_, font);
965         int const inset_x = font.isVisibleRightToLeft()
966                 ? (cursor.ix() - width) : cursor.ix();
967
968         return Box(
969                 inset_x + inset.scroll(),
970                 inset_x + width,
971                 cursor.iy() - inset.ascent(bv_, font),
972                 cursor.iy() + inset.descent(bv_, font));
973 }
974
975
976 Inset * BufferView::Pimpl::checkInset(LyXText const & text,
977                                       LyXCursor const & cursor,
978                                       int & x, int & y) const
979 {
980         pos_type const pos(cursor.pos());
981         Paragraph /*const*/ & par(*cursor.par());
982
983         if (pos >= par.size() || !par.isInset(pos)) {
984                 return 0;
985         }
986
987         Inset /*const*/ * inset = par.getInset(pos);
988
989         if (!isEditableInset(inset)) {
990                 return 0;
991         }
992
993         Box b(insetDimensions(text, cursor));
994
995         if (!b.contained(x, y)) {
996                 lyxerr[Debug::GUI] << "Missed inset at x,y " << x << "," << y
997                         << " box " << b << endl;
998                 return 0;
999         }
1000
1001         text.setCursor(bv_, &par, pos, true);
1002
1003         x -= b.x1;
1004         // The origin of an inset is on the baseline
1005         y -= text.cursor.iy();
1006
1007         return inset;
1008 }
1009
1010
1011 Inset * BufferView::Pimpl::checkInsetHit(LyXText * text, int & x, int & y)
1012 {
1013         int y_tmp = y + text->first_y;
1014
1015         LyXCursor cursor;
1016         text->setCursorFromCoordinates(bv_, cursor, x, y_tmp);
1017
1018         Inset * inset(checkInset(*text, cursor, x, y_tmp));
1019
1020         if (inset) {
1021                 y = y_tmp;
1022                 return inset;
1023         }
1024
1025         // look at previous position
1026
1027         if (cursor.pos() == 0) {
1028                 return 0;
1029         }
1030
1031         // move back one
1032         text->setCursor(bv_, cursor, cursor.par(), cursor.pos() - 1, true);
1033
1034         inset = checkInset(*text, cursor, x, y_tmp);
1035         if (inset) {
1036                 y = y_tmp;
1037         }
1038         return inset;
1039 }
1040
1041
1042 void BufferView::Pimpl::workAreaExpose()
1043 {
1044         static int work_area_width;
1045         static unsigned int work_area_height;
1046
1047         bool const widthChange = workarea().workWidth() != work_area_width;
1048         bool const heightChange = workarea().height() != work_area_height;
1049
1050         // update from work area
1051         work_area_width = workarea().workWidth();
1052         work_area_height = workarea().height();
1053         if (buffer_ != 0) {
1054                 if (widthChange) {
1055                         // The visible LyXView need a resize
1056                         owner_->resize();
1057
1058                         // Remove all texts from the textcache
1059                         // This is not _really_ what we want to do. What
1060                         // we really want to do is to delete in textcache
1061                         // that does not have a BufferView with matching
1062                         // width, but as long as we have only one BufferView
1063                         // deleting all gives the same result.
1064                         if (lyxerr.debugging())
1065                                 textcache.show(lyxerr, "Expose delete all");
1066                         textcache.clear();
1067                         buffer_->resizeInsets(bv_);
1068                 } else if (heightChange) {
1069                         // Rebuild image of current screen
1070                         updateScreen();
1071                         // fitCursor() ensures we don't jump back
1072                         // to the start of the document on vertical
1073                         // resize
1074                         fitCursor();
1075
1076                         // The main window size has changed, repaint most stuff
1077                         redraw();
1078                 } else {
1079                         screen().redraw(bv_->text, bv_);
1080                 }
1081         } else {
1082                 // Grey box when we don't have a buffer
1083                 workarea().greyOut();
1084         }
1085
1086         // always make sure that the scrollbar is sane.
1087         updateScrollbar();
1088         owner_->updateLayoutChoice();
1089         return;
1090 }
1091
1092
1093 void BufferView::Pimpl::update()
1094 {
1095         if (!bv_->theLockingInset() || !bv_->theLockingInset()->nodraw()) {
1096                 LyXText::text_status st = bv_->text->status();
1097                 screen().update(bv_->text, bv_);
1098                 bool fitc = false;
1099                 while (bv_->text->status() == LyXText::CHANGED_IN_DRAW) {
1100                         bv_->text->fullRebreak(bv_);
1101                         st = LyXText::NEED_MORE_REFRESH;
1102                         bv_->text->setCursor(bv_, bv_->text->cursor.par(),
1103                                              bv_->text->cursor.pos());
1104                         if (bv_->text->selection.set()) {
1105                                 bv_->text->setCursor(bv_, bv_->text->selection.start,
1106                                                      bv_->text->selection.start.par(),
1107                                                      bv_->text->selection.start.pos());
1108                                 bv_->text->setCursor(bv_, bv_->text->selection.end,
1109                                                      bv_->text->selection.end.par(),
1110                                                      bv_->text->selection.end.pos());
1111                         }
1112                         fitc = true;
1113                         bv_->text->status(bv_, st);
1114                         screen().update(bv_->text, bv_);
1115                 }
1116                 // do this here instead of in the screen::update because of
1117                 // the above loop!
1118                 bv_->text->status(bv_, LyXText::UNCHANGED);
1119                 if (fitc)
1120                         fitCursor();
1121         }
1122 }
1123
1124 // Values used when calling update:
1125 // -3 - update
1126 // -2 - update, move sel_cursor if selection, fitcursor
1127 // -1 - update, move sel_cursor if selection, fitcursor, mark dirty
1128 //  0 - update, move sel_cursor if selection, fitcursor
1129 //  1 - update, move sel_cursor if selection, fitcursor, mark dirty
1130 //  3 - update, move sel_cursor if selection
1131 //
1132 // update -
1133 // a simple redraw of the parts that need refresh
1134 //
1135 // move sel_cursor if selection -
1136 // the text's sel_cursor is moved if there is selection is progress
1137 //
1138 // fitcursor -
1139 // fitCursor() is called and the scrollbar updated
1140 //
1141 // mark dirty -
1142 // the buffer is marked dirty.
1143 //
1144 // enum {
1145 //       UPDATE = 0,
1146 //       SELECT = 1,
1147 //       FITCUR = 2,
1148 //       CHANGE = 4
1149 // };
1150 //
1151 // UPDATE_ONLY = UPDATE;
1152 // UPDATE_SELECT = UPDATE | SELECT;
1153 // UPDATE_SELECT_MOVE = UPDATE | SELECT | FITCUR;
1154 // UPDATE_SELECT_MOVE_AFTER_CHANGE = UPDATE | SELECT | FITCUR | CHANGE;
1155 //
1156 // update(-3) -> update(0)         -> update(0) -> update(UPDATE)
1157 // update(-2) -> update(1 + 2)     -> update(3) -> update(SELECT|FITCUR)
1158 // update(-1) -> update(1 + 2 + 4) -> update(7) -> update(SELECT|FITCUR|CHANGE)
1159 // update(1)  -> update(1 + 2 + 4) -> update(7) -> update(SELECT|FITCUR|CHANGE)
1160 // update(3)  -> update(1)         -> update(1) -> update(SELECT)
1161
1162 void BufferView::Pimpl::update(LyXText * text, BufferView::UpdateCodes f)
1163 {
1164         owner_->updateLayoutChoice();
1165
1166         if (!text->selection.set() && (f & SELECT)) {
1167                 text->selection.cursor = text->cursor;
1168         }
1169
1170         text->fullRebreak(bv_);
1171
1172         if (text->inset_owner) {
1173                 text->inset_owner->setUpdateStatus(bv_, InsetText::NONE);
1174             updateInset(text->inset_owner, false);
1175         } else {
1176             update();
1177         }
1178
1179         if ((f & FITCUR)) {
1180                 fitCursor();
1181         }
1182
1183         if ((f & CHANGE)) {
1184                 buffer_->markDirty();
1185         }
1186 }
1187
1188
1189 // Callback for cursor timer
1190 void BufferView::Pimpl::cursorToggle()
1191 {
1192         if (!buffer_) {
1193                 cursor_timeout.restart();
1194                 return;
1195         }
1196
1197         /* FIXME */
1198         extern void reapSpellchecker(void);
1199         reapSpellchecker();
1200
1201         if (!bv_->theLockingInset()) {
1202                 screen().cursorToggle(bv_);
1203         } else {
1204                 bv_->theLockingInset()->toggleInsetCursor(bv_);
1205         }
1206
1207         cursor_timeout.restart();
1208 }
1209
1210
1211 void BufferView::Pimpl::cursorPrevious(LyXText * text)
1212 {
1213         if (!text->cursor.row()->previous()) {
1214                 if (text->first_y > 0) {
1215                         int new_y = bv_->text->first_y - workarea().height();
1216                         screen().draw(bv_->text, bv_, new_y < 0 ? 0 : new_y);
1217                         updateScrollbar();
1218                 }
1219                 return;
1220         }
1221
1222         int y = text->first_y;
1223         Row * cursorrow = text->cursor.row();
1224
1225         text->setCursorFromCoordinates(bv_, text->cursor.x_fix(), y);
1226         finishUndo();
1227
1228         int new_y;
1229         if (cursorrow == bv_->text->cursor.row()) {
1230                 // we have a row which is higher than the workarea so we leave the
1231                 // cursor on the start of the row and move only the draw up as soon
1232                 // as we move the cursor or do something while inside the row (it may
1233                 // span several workarea-heights) we'll move to the top again, but this
1234                 // is better than just jump down and only display part of the row.
1235                 new_y = bv_->text->first_y - workarea().height();
1236         } else {
1237                 if (text->inset_owner) {
1238                         new_y = bv_->text->cursor.iy()
1239                                 + bv_->theLockingInset()->insetInInsetY() + y
1240                                 + text->cursor.row()->height()
1241                                 - workarea().height() + 1;
1242                 } else {
1243                         new_y = text->cursor.y()
1244                                 - text->cursor.row()->baseline()
1245                                 + text->cursor.row()->height()
1246                                 - workarea().height() + 1;
1247                 }
1248         }
1249         screen().draw(bv_->text, bv_,  new_y < 0 ? 0 : new_y);
1250         if (text->cursor.row()->previous()) {
1251                 LyXCursor cur;
1252                 text->setCursor(bv_, cur, text->cursor.row()->previous()->par(),
1253                                                 text->cursor.row()->previous()->pos(), false);
1254                 if (cur.y() > text->first_y) {
1255                         text->cursorUp(bv_, true);
1256                 }
1257         }
1258         updateScrollbar();
1259 }
1260
1261
1262 void BufferView::Pimpl::cursorNext(LyXText * text)
1263 {
1264         if (!text->cursor.row()->next()) {
1265                 int y = text->cursor.y() - text->cursor.row()->baseline() +
1266                         text->cursor.row()->height();
1267                 if (y > int(text->first_y + workarea().height())) {
1268                         screen().draw(bv_->text, bv_,
1269                                                   bv_->text->first_y + workarea().height());
1270                         updateScrollbar();
1271                 }
1272                 return;
1273         }
1274
1275         int y = text->first_y + workarea().height();
1276         if (text->inset_owner && !text->first_y) {
1277                 y -= (bv_->text->cursor.iy()
1278                           - bv_->text->first_y
1279                           + bv_->theLockingInset()->insetInInsetY());
1280         }
1281         text->getRowNearY(y);
1282
1283         Row * cursorrow = text->cursor.row();
1284         text->setCursorFromCoordinates(bv_, text->cursor.x_fix(), y); // + workarea().height());
1285         finishUndo();
1286         int new_y;
1287         if (cursorrow == bv_->text->cursor.row()) {
1288                 // we have a row which is higher than the workarea so we leave the
1289                 // cursor on the start of the row and move only the draw down as soon
1290                 // as we move the cursor or do something while inside the row (it may
1291                 // span several workarea-heights) we'll move to the top again, but this
1292                 // is better than just jump down and only display part of the row.
1293                 new_y = bv_->text->first_y + workarea().height();
1294         } else {
1295                 if (text->inset_owner) {
1296                         new_y = bv_->text->cursor.iy()
1297                                 + bv_->theLockingInset()->insetInInsetY()
1298                                 + y - text->cursor.row()->baseline();
1299                 } else {
1300                         new_y =  text->cursor.y() - text->cursor.row()->baseline();
1301                 }
1302         }
1303         screen().draw(bv_->text, bv_, new_y);
1304         if (text->cursor.row()->next()) {
1305                 LyXCursor cur;
1306                 text->setCursor(bv_, cur, text->cursor.row()->next()->par(),
1307                                                 text->cursor.row()->next()->pos(), false);
1308                 if (cur.y() < int(text->first_y + workarea().height())) {
1309                         text->cursorDown(bv_, true);
1310                 }
1311         }
1312         updateScrollbar();
1313 }
1314
1315
1316 bool BufferView::Pimpl::available() const
1317 {
1318         if (buffer_ && bv_->text)
1319                 return true;
1320         return false;
1321 }
1322
1323
1324 void BufferView::Pimpl::beforeChange(LyXText * text)
1325 {
1326         toggleSelection();
1327         text->clearSelection();
1328 }
1329
1330
1331 void BufferView::Pimpl::savePosition(unsigned int i)
1332 {
1333         if (i >= saved_positions_num)
1334                 return;
1335         saved_positions[i] = Position(buffer_->fileName(),
1336                                       bv_->text->cursor.par()->id(),
1337                                       bv_->text->cursor.pos());
1338         if (i > 0) {
1339                 ostringstream str;
1340                 str << _("Saved bookmark") << ' ' << i;
1341                 owner_->message(str.str().c_str());
1342         }
1343 }
1344
1345
1346 void BufferView::Pimpl::restorePosition(unsigned int i)
1347 {
1348         if (i >= saved_positions_num)
1349                 return;
1350
1351         string const fname = saved_positions[i].filename;
1352
1353         beforeChange(bv_->text);
1354
1355         if (fname != buffer_->fileName()) {
1356                 Buffer * b = bufferlist.exists(fname) ?
1357                         bufferlist.getBuffer(fname) :
1358                         bufferlist.loadLyXFile(fname); // don't ask, just load it
1359                 if (b != 0) buffer(b);
1360         }
1361
1362         Paragraph * par = buffer_->getParFromID(saved_positions[i].par_id);
1363         if (!par)
1364                 return;
1365
1366         bv_->text->setCursor(bv_, par,
1367                              min(par->size(), saved_positions[i].par_pos));
1368
1369         update(bv_->text, BufferView::SELECT|BufferView::FITCUR);
1370         if (i > 0) {
1371                 ostringstream str;
1372                 str << _("Moved to bookmark") << ' ' << i;
1373                 owner_->message(str.str().c_str());
1374         }
1375 }
1376
1377
1378 bool BufferView::Pimpl::isSavedPosition(unsigned int i)
1379 {
1380         if (i >= saved_positions_num)
1381                 return false;
1382
1383         return !saved_positions[i].filename.empty();
1384 }
1385
1386
1387 void BufferView::Pimpl::setState()
1388 {
1389         if (!lyxrc.rtl_support)
1390                 return;
1391
1392         LyXText * text = bv_->getLyXText();
1393         if (text->real_current_font.isRightToLeft()
1394             && !(bv_->theLockingInset()
1395                  && bv_->theLockingInset()->lyxCode()== Inset::ERT_CODE))
1396         {
1397                 if (owner_->getIntl()->keymap == Intl::PRIMARY)
1398                         owner_->getIntl()->KeyMapSec();
1399         } else {
1400                 if (owner_->getIntl()->keymap == Intl::SECONDARY)
1401                         owner_->getIntl()->KeyMapPrim();
1402         }
1403 }
1404
1405
1406 #if 0
1407 void BufferView::Pimpl::insetSleep()
1408 {
1409         if (bv_->theLockingInset() && !inset_slept) {
1410                 bv_->theLockingInset()->getCursorPos(bv_, bv_->slx, bv_->sly);
1411                 bv_->theLockingInset()->insetUnlock(bv_);
1412                 inset_slept = true;
1413         }
1414 }
1415
1416
1417 void BufferView::Pimpl::insetWakeup()
1418 {
1419         if (bv_->theLockingInset() && inset_slept) {
1420                 bv_->theLockingInset()->edit(bv_, bv_->slx, bv_->sly, 0);
1421                 inset_slept = false;
1422         }
1423 }
1424 #endif
1425
1426
1427 void BufferView::Pimpl::insetUnlock()
1428 {
1429         if (bv_->theLockingInset()) {
1430                 if (!inset_slept)
1431                         bv_->theLockingInset()->insetUnlock(bv_);
1432                 bv_->theLockingInset(0);
1433                 finishUndo();
1434                 inset_slept = false;
1435         }
1436 }
1437
1438
1439 bool BufferView::Pimpl::focus() const
1440 {
1441         return workarea().hasFocus();
1442 }
1443
1444
1445 void BufferView::Pimpl::focus(bool f)
1446 {
1447         if (f) workarea().setFocus();
1448 }
1449
1450
1451 void BufferView::Pimpl::showCursor()
1452 {
1453         if (bv_->theLockingInset())
1454                 bv_->theLockingInset()->showInsetCursor(bv_);
1455         else
1456                 screen().showCursor(bv_->text, bv_);
1457 }
1458
1459
1460 void BufferView::Pimpl::hideCursor()
1461 {
1462         if (!bv_->theLockingInset())
1463                 screen().hideCursor();
1464 }
1465
1466
1467 void BufferView::Pimpl::toggleSelection(bool b)
1468 {
1469         if (bv_->theLockingInset())
1470                 bv_->theLockingInset()->toggleSelection(bv_, b);
1471         screen().toggleSelection(bv_->text, bv_, b);
1472 }
1473
1474
1475 void BufferView::Pimpl::toggleToggle()
1476 {
1477         screen().toggleToggle(bv_->text, bv_);
1478 }
1479
1480
1481 void BufferView::Pimpl::center()
1482 {
1483         beforeChange(bv_->text);
1484         if (bv_->text->cursor.y() > static_cast<int>((workarea().height() / 2))) {
1485                 screen().draw(bv_->text, bv_, bv_->text->cursor.y() - workarea().height() / 2);
1486         } else {
1487                 screen().draw(bv_->text, bv_, 0);
1488         }
1489         update(bv_->text, BufferView::SELECT|BufferView::FITCUR);
1490         redraw();
1491 }
1492
1493
1494 void BufferView::Pimpl::pasteClipboard(bool asPara)
1495 {
1496         if (!buffer_)
1497                 return;
1498
1499         screen().hideCursor();
1500         beforeChange(bv_->text);
1501
1502         string const clip(workarea().getClipboard());
1503
1504         if (clip.empty())
1505                 return;
1506
1507         if (asPara) {
1508                 bv_->getLyXText()->insertStringAsParagraphs(bv_, clip);
1509         } else {
1510                 bv_->getLyXText()->insertStringAsLines(bv_, clip);
1511         }
1512         bv_->getLyXText()->clearSelection();
1513         update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
1514 }
1515
1516
1517 void BufferView::Pimpl::stuffClipboard(string const & stuff) const
1518 {
1519         workarea().putClipboard(stuff);
1520 }
1521
1522
1523 /*
1524  * Dispatch functions for actions which can be valid for BufferView->text
1525  * and/or InsetText->text!!!
1526  */
1527
1528
1529 inline
1530 void BufferView::Pimpl::moveCursorUpdate(bool selecting, bool fitcur)
1531 {
1532         LyXText * lt = bv_->getLyXText();
1533
1534         if (selecting || lt->selection.mark()) {
1535                 lt->setSelection(bv_);
1536                 if (lt->bv_owner)
1537                         toggleToggle();
1538                 else
1539                         updateInset(lt->inset_owner, false);
1540         }
1541         if (lt->bv_owner) {
1542                 if (fitcur)
1543                         update(lt, BufferView::SELECT|BufferView::FITCUR);
1544                 else
1545                         update(lt, BufferView::SELECT);
1546                 showCursor();
1547         } else if (bv_->text->status() != LyXText::UNCHANGED) {
1548                 bv_->theLockingInset()->hideInsetCursor(bv_);
1549                 update(bv_->text, BufferView::SELECT|BufferView::FITCUR);
1550                 showCursor();
1551         }
1552
1553         if (!lt->selection.set())
1554                 workarea().haveSelection(false);
1555
1556         /* ---> Everytime the cursor is moved, show the current font state. */
1557         // should this too me moved out of this func?
1558         //owner->showState();
1559         setState();
1560 }
1561
1562
1563 Inset * BufferView::Pimpl::getInsetByCode(Inset::Code code)
1564 {
1565         LyXCursor cursor = bv_->getLyXText()->cursor;
1566         Buffer::inset_iterator it =
1567                 find_if(Buffer::inset_iterator(
1568                         cursor.par(), cursor.pos()),
1569                         buffer_->inset_iterator_end(),
1570                         lyx::compare_memfun(&Inset::lyxCode, code));
1571         return it != buffer_->inset_iterator_end() ? (*it) : 0;
1572 }
1573
1574
1575 void BufferView::Pimpl::MenuInsertLyXFile(string const & filen)
1576 {
1577         string filename = filen;
1578
1579         if (filename.empty()) {
1580                 // Launch a file browser
1581                 string initpath = lyxrc.document_path;
1582
1583                 if (available()) {
1584                         string const trypath = owner_->buffer()->filePath();
1585                         // If directory is writeable, use this as default.
1586                         if (IsDirWriteable(trypath))
1587                                 initpath = trypath;
1588                 }
1589
1590                 FileDialog fileDlg(bv_->owner(),
1591                                    _("Select LyX document to insert"),
1592                         LFUN_FILE_INSERT,
1593                         make_pair(string(_("Documents|#o#O")),
1594                                   string(lyxrc.document_path)),
1595                         make_pair(string(_("Examples|#E#e")),
1596                                   string(AddPath(system_lyxdir, "examples"))));
1597
1598                 FileDialog::Result result =
1599                         fileDlg.Select(initpath,
1600                                        _("*.lyx| LyX Documents (*.lyx)"));
1601
1602                 if (result.first == FileDialog::Later)
1603                         return;
1604
1605                 filename = result.second;
1606
1607                 // check selected filename
1608                 if (filename.empty()) {
1609                         owner_->message(_("Canceled."));
1610                         return;
1611                 }
1612         }
1613
1614         // get absolute path of file and add ".lyx" to the filename if
1615         // necessary
1616         filename = FileSearch(string(), filename, "lyx");
1617
1618         string const disp_fn(MakeDisplayPath(filename));
1619
1620         ostringstream s1;
1621         s1 << _("Inserting document") << ' '
1622            << disp_fn << " ...";
1623         owner_->message(s1.str().c_str());
1624         bool const res = bv_->insertLyXFile(filename);
1625         if (res) {
1626                 ostringstream str;
1627                 str << _("Document") << ' ' << disp_fn
1628                     << ' ' << _("inserted.");
1629                 owner_->message(str.str().c_str());
1630         } else {
1631                 ostringstream str;
1632                 str << _("Could not insert document") << ' '
1633                     << disp_fn;
1634                 owner_->message(str.str().c_str());
1635         }
1636 }
1637
1638
1639 bool BufferView::Pimpl::Dispatch(kb_action action, string const & argument)
1640 {
1641         lyxerr[Debug::ACTION] << "BufferView::Pimpl::Dispatch: action["
1642                               << action <<"] arg[" << argument << "]" << endl;
1643
1644         LyXTextClass const & tclass = textclasslist[buffer_->params.textclass];
1645
1646         switch (action) {
1647                 // --- Misc -------------------------------------------
1648         case LFUN_APPENDIX:
1649         {
1650                 if (available()) {
1651                         LyXText * lt = bv_->getLyXText();
1652                         lt->toggleAppendix(bv_);
1653                         update(lt,
1654                                BufferView::SELECT
1655                                | BufferView::FITCUR
1656                                | BufferView::CHANGE);
1657                 }
1658         }
1659         break;
1660
1661         case LFUN_TOC_INSERT:
1662         {
1663                 InsetCommandParams p;
1664                 p.setCmdName("tableofcontents");
1665                 Inset * inset = new InsetTOC(p);
1666                 if (!insertInset(inset, tclass.defaultLayoutName()))
1667                         delete inset;
1668                 break;
1669         }
1670
1671         case LFUN_SCROLL_INSET:
1672                 // this is not handled here as this funktion is only aktive
1673                 // if we have a locking_inset and that one is (or contains)
1674                 // a tabular-inset
1675                 break;
1676
1677         case LFUN_INSET_GRAPHICS:
1678         {
1679                 Inset * new_inset = new InsetGraphics;
1680                 if (!insertInset(new_inset)) {
1681                         delete new_inset;
1682                 } else {
1683                         // this is need because you don't use a inset->Edit()
1684                         updateInset(new_inset, true);
1685                         new_inset->edit(bv_);
1686                 }
1687                 break;
1688         }
1689
1690         case LFUN_PASTE:
1691                 bv_->paste();
1692                 setState();
1693                 break;
1694
1695         case LFUN_PASTESELECTION:
1696         {
1697                 bool asPara = false;
1698                 if (argument == "paragraph")
1699                         asPara = true;
1700                 pasteClipboard(asPara);
1701         }
1702         break;
1703
1704         case LFUN_CUT:
1705                 bv_->cut();
1706                 break;
1707
1708         case LFUN_COPY:
1709                 bv_->copy();
1710                 break;
1711
1712         case LFUN_LAYOUT_COPY:
1713                 bv_->copyEnvironment();
1714                 break;
1715
1716         case LFUN_LAYOUT_PASTE:
1717                 bv_->pasteEnvironment();
1718                 setState();
1719                 break;
1720
1721         case LFUN_GOTOERROR:
1722                 gotoInset(Inset::ERROR_CODE, false);
1723                 break;
1724
1725         case LFUN_GOTONOTE:
1726                 gotoInset(Inset::IGNORE_CODE, false);
1727                 break;
1728
1729         case LFUN_REFERENCE_GOTO:
1730         {
1731                 vector<Inset::Code> tmp;
1732                 tmp.push_back(Inset::LABEL_CODE);
1733                 tmp.push_back(Inset::REF_CODE);
1734                 gotoInset(tmp, true);
1735                 break;
1736         }
1737
1738         case LFUN_HYPHENATION:
1739                 specialChar(InsetSpecialChar::HYPHENATION);
1740                 break;
1741
1742         case LFUN_LIGATURE_BREAK:
1743                 specialChar(InsetSpecialChar::LIGATURE_BREAK);
1744                 break;
1745
1746         case LFUN_LDOTS:
1747                 specialChar(InsetSpecialChar::LDOTS);
1748                 break;
1749
1750         case LFUN_END_OF_SENTENCE:
1751                 specialChar(InsetSpecialChar::END_OF_SENTENCE);
1752                 break;
1753
1754         case LFUN_MENU_SEPARATOR:
1755                 specialChar(InsetSpecialChar::MENU_SEPARATOR);
1756                 break;
1757
1758         case LFUN_HFILL:
1759                 hfill();
1760                 break;
1761
1762         case LFUN_DEPTH_MIN:
1763                 changeDepth(bv_, bv_->getLyXText(), -1);
1764                 break;
1765
1766         case LFUN_DEPTH_PLUS:
1767                 changeDepth(bv_, bv_->getLyXText(), 1);
1768                 break;
1769
1770         case LFUN_FREE:
1771                 owner_->getDialogs()->setUserFreeFont();
1772                 break;
1773
1774         case LFUN_FILE_INSERT:
1775                 MenuInsertLyXFile(argument);
1776                 break;
1777
1778         case LFUN_FILE_INSERT_ASCII_PARA:
1779                 InsertAsciiFile(bv_, argument, true);
1780                 break;
1781
1782         case LFUN_FILE_INSERT_ASCII:
1783                 InsertAsciiFile(bv_, argument, false);
1784                 break;
1785
1786         case LFUN_LAYOUT:
1787         {
1788                 lyxerr[Debug::INFO] << "LFUN_LAYOUT: (arg) "
1789                                     << argument << endl;
1790
1791                 // This is not the good solution to the empty argument
1792                 // problem, but it will hopefully suffice for 1.2.0.
1793                 // The correct solution would be to augument the
1794                 // function list/array with information about what
1795                 // functions needs arguments and their type.
1796                 if (argument.empty()) {
1797                         owner_->getLyXFunc()->setErrorMessage(
1798                                 _("LyX function 'layout' needs an argument."));
1799                         break;
1800                 }
1801
1802                 // Derive layout number from given argument (string)
1803                 // and current buffer's textclass (number). */
1804                 bool hasLayout = tclass.hasLayout(argument);
1805                 string layout = argument;
1806
1807                 // If the entry is obsolete, use the new one instead.
1808                 if (hasLayout) {
1809                         string const & obs = tclass[layout].obsoleted_by();
1810                         if (!obs.empty())
1811                                 layout = obs;
1812                 }
1813
1814                 if (!hasLayout) {
1815                         owner_->getLyXFunc()->setErrorMessage(
1816                                 string(N_("Layout ")) + argument +
1817                                 N_(" not known"));
1818                         break;
1819                 }
1820
1821                 bool change_layout = (current_layout != layout);
1822                 LyXText * lt = bv_->getLyXText();
1823                 if (!change_layout && lt->selection.set() &&
1824                         lt->selection.start.par() != lt->selection.end.par())
1825                 {
1826                         Paragraph * spar = lt->selection.start.par();
1827                         Paragraph * epar = lt->selection.end.par()->next();
1828                         while(spar != epar) {
1829                                 if (spar->layout() != current_layout) {
1830                                         change_layout = true;
1831                                         break;
1832                                 }
1833                         }
1834                 }
1835                 if (change_layout) {
1836                         hideCursor();
1837                         current_layout = layout;
1838                         update(lt,
1839                                BufferView::SELECT
1840                                | BufferView::FITCUR);
1841                         lt->setLayout(bv_, layout);
1842                         owner_->setLayout(layout);
1843                         update(lt,
1844                                BufferView::SELECT
1845                                | BufferView::FITCUR
1846                                | BufferView::CHANGE);
1847                         setState();
1848                 }
1849         }
1850         break;
1851
1852         case LFUN_LANGUAGE:
1853                 lang(bv_, argument);
1854                 setState();
1855                 owner_->showState();
1856                 break;
1857
1858         case LFUN_EMPH:
1859                 emph(bv_);
1860                 owner_->showState();
1861                 break;
1862
1863         case LFUN_BOLD:
1864                 bold(bv_);
1865                 owner_->showState();
1866                 break;
1867
1868         case LFUN_NOUN:
1869                 noun(bv_);
1870                 owner_->showState();
1871                 break;
1872
1873         case LFUN_CODE:
1874                 code(bv_);
1875                 owner_->showState();
1876                 break;
1877
1878         case LFUN_SANS:
1879                 sans(bv_);
1880                 owner_->showState();
1881                 break;
1882
1883         case LFUN_ROMAN:
1884                 roman(bv_);
1885                 owner_->showState();
1886                 break;
1887
1888         case LFUN_DEFAULT:
1889                 styleReset(bv_);
1890                 owner_->showState();
1891                 break;
1892
1893         case LFUN_UNDERLINE:
1894                 underline(bv_);
1895                 owner_->showState();
1896                 break;
1897
1898         case LFUN_FONT_SIZE:
1899                 fontSize(bv_, argument);
1900                 owner_->showState();
1901                 break;
1902
1903         case LFUN_FONT_STATE:
1904                 owner_->getLyXFunc()->setMessage(currentState(bv_));
1905                 break;
1906
1907         case LFUN_UPCASE_WORD:
1908         {
1909                 LyXText * lt = bv_->getLyXText();
1910
1911                 update(lt,
1912                        BufferView::SELECT
1913                        | BufferView::FITCUR);
1914                 lt->changeCase(bv_, LyXText::text_uppercase);
1915                 if (lt->inset_owner)
1916                         updateInset(lt->inset_owner, true);
1917                 update(lt,
1918                        BufferView::SELECT
1919                        | BufferView::FITCUR
1920                        | BufferView::CHANGE);
1921         }
1922         break;
1923
1924         case LFUN_LOWCASE_WORD:
1925         {
1926                 LyXText * lt = bv_->getLyXText();
1927
1928                 update(lt, BufferView::SELECT|BufferView::FITCUR);
1929                 lt->changeCase(bv_, LyXText::text_lowercase);
1930                 if (lt->inset_owner)
1931                         updateInset(lt->inset_owner, true);
1932                 update(lt,
1933                        BufferView::SELECT
1934                        | BufferView::FITCUR
1935                        | BufferView::CHANGE);
1936         }
1937         break;
1938
1939         case LFUN_CAPITALIZE_WORD:
1940         {
1941                 LyXText * lt = bv_->getLyXText();
1942
1943                 update(lt, BufferView::SELECT|BufferView::FITCUR);
1944                 lt->changeCase(bv_, LyXText::text_capitalization);
1945                 if (lt->inset_owner)
1946                         updateInset(lt->inset_owner, true);
1947                 update(lt,
1948                        BufferView::SELECT
1949                        | BufferView::FITCUR
1950                        | BufferView::CHANGE);
1951         }
1952         break;
1953
1954         case LFUN_TRANSPOSE_CHARS:
1955         {
1956                 LyXText * lt = bv_->getLyXText();
1957
1958                 update(lt, BufferView::SELECT|BufferView::FITCUR);
1959                 lt->transposeChars(*bv_);
1960                 if (lt->inset_owner)
1961                         updateInset(lt->inset_owner, true);
1962                 update(lt,
1963                        BufferView::SELECT
1964                        | BufferView::FITCUR
1965                        | BufferView::CHANGE);
1966         }
1967         break;
1968
1969
1970         case LFUN_INSERT_LABEL:
1971                 MenuInsertLabel(bv_, argument);
1972                 break;
1973
1974         case LFUN_REF_INSERT:
1975                 if (argument.empty()) {
1976                         InsetCommandParams p("ref");
1977                         owner_->getDialogs()->createRef(p.getAsString());
1978                 } else {
1979                         InsetCommandParams p;
1980                         p.setFromString(argument);
1981
1982                         InsetRef * inset = new InsetRef(p, *buffer_);
1983                         if (!insertInset(inset))
1984                                 delete inset;
1985                         else
1986                                 updateInset(inset, true);
1987                 }
1988                 break;
1989
1990         case LFUN_BOOKMARK_SAVE:
1991                 savePosition(strToUnsignedInt(argument));
1992                 break;
1993
1994         case LFUN_BOOKMARK_GOTO:
1995                 restorePosition(strToUnsignedInt(argument));
1996                 break;
1997
1998         case LFUN_REF_GOTO:
1999         {
2000                 string label(argument);
2001                 if (label.empty()) {
2002                         InsetRef * inset =
2003                                 static_cast<InsetRef*>(getInsetByCode(Inset::REF_CODE));
2004                         if (inset) {
2005                                 label = inset->getContents();
2006                                 savePosition(0);
2007                         }
2008                 }
2009
2010                 if (!label.empty()) {
2011                         //bv_->savePosition(0);
2012                         if (!bv_->gotoLabel(label))
2013                                 Alert::alert(_("Error"),
2014                                            _("Couldn't find this label"),
2015                                            _("in current document."));
2016                 }
2017         }
2018         break;
2019
2020                 // --- Cursor Movements -----------------------------
2021         case LFUN_RIGHT:
2022         {
2023                 LyXText * lt = bv_->getLyXText();
2024
2025                 bool is_rtl = lt->cursor.par()->isRightToLeftPar(buffer_->params);
2026                 if (!lt->selection.mark())
2027                         beforeChange(lt);
2028                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2029                 if (is_rtl)
2030                         lt->cursorLeft(bv_, false);
2031                 if (lt->cursor.pos() < lt->cursor.par()->size()
2032                     && lt->cursor.par()->isInset(lt->cursor.pos())
2033                     && isHighlyEditableInset(lt->cursor.par()->getInset(lt->cursor.pos()))) {
2034                         Inset * tmpinset = lt->cursor.par()->getInset(lt->cursor.pos());
2035                         owner_->getLyXFunc()->setMessage(tmpinset->editMessage());
2036                         if (is_rtl)
2037                                 tmpinset->edit(bv_, false);
2038                         else
2039                                 tmpinset->edit(bv_);
2040                         break;
2041                 }
2042                 if (!is_rtl)
2043                         lt->cursorRight(bv_, false);
2044                 finishUndo();
2045                 moveCursorUpdate(false);
2046                 owner_->showState();
2047         }
2048         break;
2049
2050         case LFUN_LEFT:
2051         {
2052                 // This is soooo ugly. Isn`t it possible to make
2053                 // it simpler? (Lgb)
2054                 LyXText * lt = bv_->getLyXText();
2055                 bool is_rtl = lt->cursor.par()->isRightToLeftPar(buffer_->params);
2056                 if (!lt->selection.mark())
2057                         beforeChange(lt);
2058                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2059                 LyXCursor const cur = lt->cursor;
2060                 if (!is_rtl)
2061                         lt->cursorLeft(bv_, false);
2062                 if ((is_rtl || cur != lt->cursor) && // only if really moved!
2063                     lt->cursor.pos() < lt->cursor.par()->size() &&
2064                     lt->cursor.par()->isInset(lt->cursor.pos()) &&
2065                     isHighlyEditableInset(lt->cursor.par()->getInset(lt->cursor.pos()))) {
2066                         Inset * tmpinset = lt->cursor.par()->getInset(lt->cursor.pos());
2067                         owner_->getLyXFunc()->setMessage(tmpinset->editMessage());
2068                         if (is_rtl)
2069                                 tmpinset->edit(bv_);
2070                         else
2071                                 tmpinset->edit(bv_, false);
2072                         break;
2073                 }
2074                 if  (is_rtl)
2075                         lt->cursorRight(bv_, false);
2076
2077                 finishUndo();
2078                 moveCursorUpdate(false);
2079                 owner_->showState();
2080         }
2081         break;
2082
2083         case LFUN_UP:
2084         {
2085                 LyXText * lt = bv_->getLyXText();
2086
2087                 if (!lt->selection.mark())
2088                         beforeChange(lt);
2089                 update(lt, BufferView::UPDATE);
2090                 lt->cursorUp(bv_);
2091                 finishUndo();
2092                 moveCursorUpdate(false);
2093                 owner_->showState();
2094         }
2095         break;
2096
2097         case LFUN_DOWN:
2098         {
2099                 LyXText * lt = bv_->getLyXText();
2100
2101                 if (!lt->selection.mark())
2102                         beforeChange(lt);
2103                 update(lt, BufferView::UPDATE);
2104                 lt->cursorDown(bv_);
2105                 finishUndo();
2106                 moveCursorUpdate(false);
2107                 owner_->showState();
2108         }
2109         break;
2110
2111         case LFUN_UP_PARAGRAPH:
2112         {
2113                 LyXText * lt = bv_->getLyXText();
2114
2115                 if (!lt->selection.mark())
2116                         beforeChange(lt);
2117                 update(lt, BufferView::UPDATE);
2118                 lt->cursorUpParagraph(bv_);
2119                 finishUndo();
2120                 moveCursorUpdate(false);
2121                 owner_->showState();
2122         }
2123         break;
2124
2125         case LFUN_DOWN_PARAGRAPH:
2126         {
2127                 LyXText * lt = bv_->getLyXText();
2128
2129                 if (!lt->selection.mark())
2130                         beforeChange(lt);
2131                 update(lt, BufferView::UPDATE);
2132                 lt->cursorDownParagraph(bv_);
2133                 finishUndo();
2134                 moveCursorUpdate(false);
2135                 owner_->showState();
2136         }
2137         break;
2138
2139         case LFUN_PRIOR:
2140         {
2141                 LyXText * lt = bv_->getLyXText();
2142
2143                 if (!lt->selection.mark())
2144                         beforeChange(lt);
2145                 update(lt, BufferView::UPDATE);
2146                 cursorPrevious(lt);
2147                 finishUndo();
2148                 moveCursorUpdate(false, false);
2149                 owner_->showState();
2150         }
2151         break;
2152
2153         case LFUN_NEXT:
2154         {
2155                 LyXText * lt = bv_->getLyXText();
2156
2157                 if (!lt->selection.mark())
2158                         beforeChange(lt);
2159                 update(lt, BufferView::UPDATE);
2160                 cursorNext(lt);
2161                 finishUndo();
2162                 moveCursorUpdate(false, false);
2163                 owner_->showState();
2164         }
2165         break;
2166
2167         case LFUN_HOME:
2168         {
2169                 LyXText * lt = bv_->getLyXText();
2170
2171                 if (!lt->selection.mark())
2172                         beforeChange(lt);
2173                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2174                 lt->cursorHome(bv_);
2175                 finishUndo();
2176                 moveCursorUpdate(false);
2177                 owner_->showState();
2178         }
2179         break;
2180
2181         case LFUN_END:
2182         {
2183                 LyXText * lt = bv_->getLyXText();
2184
2185                 if (!lt->selection.mark())
2186                         beforeChange(lt);
2187                 update(lt,
2188                        BufferView::SELECT|BufferView::FITCUR);
2189                 lt->cursorEnd(bv_);
2190                 finishUndo();
2191                 moveCursorUpdate(false);
2192                 owner_->showState();
2193         }
2194         break;
2195
2196         case LFUN_SHIFT_TAB:
2197         case LFUN_TAB:
2198         {
2199                 LyXText * lt = bv_->getLyXText();
2200
2201                 if (!lt->selection.mark())
2202                         beforeChange(lt);
2203                 update(lt,
2204                        BufferView::SELECT|BufferView::FITCUR);
2205                 lt->cursorTab(bv_);
2206                 finishUndo();
2207                 moveCursorUpdate(false);
2208                 owner_->showState();
2209         }
2210         break;
2211
2212         case LFUN_WORDRIGHT:
2213         {
2214                 LyXText * lt = bv_->getLyXText();
2215
2216                 if (!lt->selection.mark())
2217                         beforeChange(lt);
2218                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2219                 if (lt->cursor.par()->isRightToLeftPar(buffer_->params))
2220                         lt->cursorLeftOneWord(bv_);
2221                 else
2222                         lt->cursorRightOneWord(bv_);
2223                 finishUndo();
2224                 moveCursorUpdate(false);
2225                 owner_->showState();
2226         }
2227         break;
2228
2229         case LFUN_WORDLEFT:
2230         {
2231                 LyXText * lt = bv_->getLyXText();
2232
2233                 if (!lt->selection.mark())
2234                         beforeChange(lt);
2235                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2236                 if (lt->cursor.par()->isRightToLeftPar(buffer_->params))
2237                         lt->cursorRightOneWord(bv_);
2238                 else
2239                         lt->cursorLeftOneWord(bv_);
2240                 finishUndo();
2241                 moveCursorUpdate(false);
2242                 owner_->showState();
2243         }
2244         break;
2245
2246         case LFUN_BEGINNINGBUF:
2247         {
2248                 LyXText * lt = bv_->getLyXText();
2249
2250                 if (!lt->selection.mark())
2251                         beforeChange(lt);
2252                 update(lt,
2253                        BufferView::SELECT|BufferView::FITCUR);
2254                 lt->cursorTop(bv_);
2255                 finishUndo();
2256                 moveCursorUpdate(false);
2257                 owner_->showState();
2258         }
2259         break;
2260
2261         case LFUN_ENDBUF:
2262         {
2263                 LyXText * lt = bv_->getLyXText();
2264
2265                 if (!lt->selection.mark())
2266                         beforeChange(lt);
2267                 update(lt,
2268                        BufferView::SELECT|BufferView::FITCUR);
2269                 lt->cursorBottom(bv_);
2270                 finishUndo();
2271                 moveCursorUpdate(false);
2272                 owner_->showState();
2273         }
2274         break;
2275
2276                 /* cursor selection ---------------------------- */
2277         case LFUN_RIGHTSEL:
2278         {
2279                 LyXText * lt = bv_->getLyXText();
2280
2281                 update(lt,
2282                        BufferView::SELECT|BufferView::FITCUR);
2283                 if (lt->cursor.par()->isRightToLeftPar(buffer_->params))
2284                         lt->cursorLeft(bv_);
2285                 else
2286                         lt->cursorRight(bv_);
2287                 finishUndo();
2288                 moveCursorUpdate(true);
2289                 owner_->showState();
2290         }
2291         break;
2292
2293         case LFUN_LEFTSEL:
2294         {
2295                 LyXText * lt = bv_->getLyXText();
2296
2297                 update(lt,
2298                        BufferView::SELECT|BufferView::FITCUR);
2299                 if (lt->cursor.par()->isRightToLeftPar(buffer_->params))
2300                         lt->cursorRight(bv_);
2301                 else
2302                         lt->cursorLeft(bv_);
2303                 finishUndo();
2304                 moveCursorUpdate(true);
2305                 owner_->showState();
2306         }
2307         break;
2308
2309         case LFUN_UPSEL:
2310         {
2311                 LyXText * lt = bv_->getLyXText();
2312
2313                 update(lt,
2314                        BufferView::SELECT|BufferView::FITCUR);
2315                 lt->cursorUp(bv_, true);
2316                 finishUndo();
2317                 moveCursorUpdate(true);
2318                 owner_->showState();
2319         }
2320         break;
2321
2322         case LFUN_DOWNSEL:
2323         {
2324                 LyXText * lt = bv_->getLyXText();
2325
2326                 update(lt,
2327                        BufferView::SELECT|BufferView::FITCUR);
2328                 lt->cursorDown(bv_, true);
2329                 finishUndo();
2330                 moveCursorUpdate(true);
2331                 owner_->showState();
2332         }
2333         break;
2334
2335         case LFUN_UP_PARAGRAPHSEL:
2336         {
2337                 LyXText * lt = bv_->getLyXText();
2338
2339                 update(lt,
2340                        BufferView::SELECT|BufferView::FITCUR);
2341                 lt->cursorUpParagraph(bv_);
2342                 finishUndo();
2343                 moveCursorUpdate(true);
2344                 owner_->showState();
2345         }
2346         break;
2347
2348         case LFUN_DOWN_PARAGRAPHSEL:
2349         {
2350                 LyXText * lt = bv_->getLyXText();
2351
2352                 update(lt,
2353                        BufferView::SELECT|BufferView::FITCUR);
2354                 lt->cursorDownParagraph(bv_);
2355                 finishUndo();
2356                 moveCursorUpdate(true);
2357                 owner_->showState();
2358         }
2359         break;
2360
2361         case LFUN_PRIORSEL:
2362         {
2363                 LyXText * lt = bv_->getLyXText();
2364
2365                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2366                 cursorPrevious(lt);
2367                 finishUndo();
2368                 moveCursorUpdate(true);
2369                 owner_->showState();
2370         }
2371         break;
2372
2373         case LFUN_NEXTSEL:
2374         {
2375                 LyXText * lt = bv_->getLyXText();
2376
2377                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2378                 cursorNext(lt);
2379                 finishUndo();
2380                 moveCursorUpdate(true);
2381                 owner_->showState();
2382         }
2383         break;
2384
2385         case LFUN_HOMESEL:
2386         {
2387                 LyXText * lt = bv_->getLyXText();
2388
2389                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2390                 lt->cursorHome(bv_);
2391                 finishUndo();
2392                 moveCursorUpdate(true);
2393                 owner_->showState();
2394         }
2395         break;
2396
2397         case LFUN_ENDSEL:
2398         {
2399                 LyXText * lt = bv_->getLyXText();
2400
2401                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2402                 lt->cursorEnd(bv_);
2403                 finishUndo();
2404                 moveCursorUpdate(true);
2405                 owner_->showState();
2406         }
2407         break;
2408
2409         case LFUN_WORDRIGHTSEL:
2410         {
2411                 LyXText * lt = bv_->getLyXText();
2412
2413                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2414                 if (lt->cursor.par()->isRightToLeftPar(buffer_->params))
2415                         lt->cursorLeftOneWord(bv_);
2416                 else
2417                         lt->cursorRightOneWord(bv_);
2418                 finishUndo();
2419                 moveCursorUpdate(true);
2420                 owner_->showState();
2421         }
2422         break;
2423
2424         case LFUN_WORDLEFTSEL:
2425         {
2426                 LyXText * lt = bv_->getLyXText();
2427
2428                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2429                 if (lt->cursor.par()->isRightToLeftPar(buffer_->params))
2430                         lt->cursorRightOneWord(bv_);
2431                 else
2432                         lt->cursorLeftOneWord(bv_);
2433                 finishUndo();
2434                 moveCursorUpdate(true);
2435                 owner_->showState();
2436         }
2437         break;
2438
2439         case LFUN_BEGINNINGBUFSEL:
2440         {
2441                 LyXText * lt = bv_->getLyXText();
2442
2443                 if (lt->inset_owner)
2444                         break;
2445                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2446                 lt->cursorTop(bv_);
2447                 finishUndo();
2448                 moveCursorUpdate(true);
2449                 owner_->showState();
2450         }
2451         break;
2452
2453         case LFUN_ENDBUFSEL:
2454         {
2455                 LyXText * lt = bv_->getLyXText();
2456
2457                 if (lt->inset_owner)
2458                         break;
2459                 update(lt,
2460                        BufferView::SELECT|BufferView::FITCUR);
2461                 lt->cursorBottom(bv_);
2462                 finishUndo();
2463                 moveCursorUpdate(true);
2464                 owner_->showState();
2465         }
2466         break;
2467
2468                 // --- text changing commands ------------------------
2469         case LFUN_BREAKLINE:
2470         {
2471                 LyXText * lt = bv_->getLyXText();
2472
2473                 beforeChange(lt);
2474                 lt->insertChar(bv_, Paragraph::META_NEWLINE);
2475                 update(lt,
2476                        BufferView::SELECT
2477                        | BufferView::FITCUR
2478                        | BufferView::CHANGE);
2479                 lt->setCursor(bv_, lt->cursor.par(), lt->cursor.pos());
2480                 moveCursorUpdate(false);
2481         }
2482         break;
2483
2484         case LFUN_PROTECTEDSPACE:
2485         {
2486                 LyXText * lt = bv_->getLyXText();
2487
2488                 LyXLayout const & style = tclass[lt->cursor.par()->layout()];
2489
2490                 if (style.free_spacing) {
2491                         lt->insertChar(bv_, ' ');
2492                         update(lt,
2493                                BufferView::SELECT
2494                                | BufferView::FITCUR
2495                                | BufferView::CHANGE);
2496                 } else {
2497                         specialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2498                 }
2499                 moveCursorUpdate(false);
2500         }
2501         break;
2502
2503         case LFUN_SETMARK:
2504         {
2505                 LyXText * lt = bv_->getLyXText();
2506
2507                 if (lt->selection.mark()) {
2508                         beforeChange(lt);
2509                         update(lt,
2510                                BufferView::SELECT
2511                                | BufferView::FITCUR);
2512                         owner_->getLyXFunc()->setMessage(N_("Mark removed"));
2513                 } else {
2514                         beforeChange(lt);
2515                         lt->selection.mark(true);
2516                         update(lt,
2517                                BufferView::SELECT
2518                                | BufferView::FITCUR);
2519                         owner_->getLyXFunc()->setMessage(N_("Mark set"));
2520                 }
2521                 lt->selection.cursor = lt->cursor;
2522         }
2523         break;
2524
2525         case LFUN_DELETE:
2526         {
2527                 LyXText * lt = bv_->getLyXText();
2528
2529                 if (!lt->selection.set()) {
2530                         lt->Delete(bv_);
2531                         lt->selection.cursor = lt->cursor;
2532                         update(lt,
2533                                BufferView::SELECT
2534                                | BufferView::FITCUR
2535                                | BufferView::CHANGE);
2536                         // It is possible to make it a lot faster still
2537                         // just comment out the line below...
2538                         showCursor();
2539                 } else {
2540                         bv_->cut(false);
2541                 }
2542                 moveCursorUpdate(false);
2543                 owner_->showState();
2544                 setState();
2545         }
2546         break;
2547
2548         case LFUN_DELETE_SKIP:
2549         {
2550                 LyXText * lt = bv_->getLyXText();
2551
2552                 // Reverse the effect of LFUN_BREAKPARAGRAPH_SKIP.
2553
2554                 LyXCursor cursor = lt->cursor;
2555
2556                 if (!lt->selection.set()) {
2557                         if (cursor.pos() == cursor.par()->size()) {
2558                                 lt->cursorRight(bv_);
2559                                 cursor = lt->cursor;
2560                                 if (cursor.pos() == 0
2561                                     && !(cursor.par()->params().spaceTop()
2562                                          == VSpace (VSpace::NONE))) {
2563                                         lt->setParagraph
2564                                                 (bv_,
2565                                                  cursor.par()->params().lineTop(),
2566                                                  cursor.par()->params().lineBottom(),
2567                                                  cursor.par()->params().pagebreakTop(),
2568                                                  cursor.par()->params().pagebreakBottom(),
2569                                                  VSpace(VSpace::NONE),
2570                                                  cursor.par()->params().spaceBottom(),
2571                                                  cursor.par()->params().spacing(),
2572                                                  cursor.par()->params().align(),
2573                                                  cursor.par()->params().labelWidthString(), 0);
2574                                         lt->cursorLeft(bv_);
2575                                         update(lt,
2576                                                BufferView::SELECT
2577                                                | BufferView::FITCUR
2578                                                | BufferView::CHANGE);
2579                                 } else {
2580                                         lt->cursorLeft(bv_);
2581                                         lt->Delete(bv_);
2582                                         lt->selection.cursor = lt->cursor;
2583                                         update(lt,
2584                                                BufferView::SELECT
2585                                                | BufferView::FITCUR
2586                                                | BufferView::CHANGE);
2587                                 }
2588                         } else {
2589                                 lt->Delete(bv_);
2590                                 lt->selection.cursor = lt->cursor;
2591                                 update(lt,
2592                                        BufferView::SELECT
2593                                        | BufferView::FITCUR
2594                                        | BufferView::CHANGE);
2595                         }
2596                 } else {
2597                         bv_->cut(false);
2598                 }
2599         }
2600         break;
2601
2602         /* -------> Delete word forward. */
2603         case LFUN_DELETE_WORD_FORWARD:
2604                 update(bv_->getLyXText(), BufferView::SELECT|BufferView::FITCUR);
2605                 bv_->getLyXText()->deleteWordForward(bv_);
2606                 update(bv_->getLyXText(), BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
2607                 moveCursorUpdate(false);
2608                 owner_->showState();
2609                 break;
2610
2611                 /* -------> Delete word backward. */
2612         case LFUN_DELETE_WORD_BACKWARD:
2613         {
2614                 LyXText * lt = bv_->getLyXText();
2615
2616                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2617                 lt->deleteWordBackward(bv_);
2618                 update(lt,
2619                        BufferView::SELECT
2620                        | BufferView::FITCUR
2621                        | BufferView::CHANGE);
2622                 moveCursorUpdate(false);
2623                 owner_->showState();
2624         }
2625         break;
2626
2627                 /* -------> Kill to end of line. */
2628         case LFUN_DELETE_LINE_FORWARD:
2629         {
2630                 LyXText * lt = bv_->getLyXText();
2631
2632                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2633                 lt->deleteLineForward(bv_);
2634                 update(lt,
2635                        BufferView::SELECT
2636                        | BufferView::FITCUR
2637                        | BufferView::CHANGE);
2638                 moveCursorUpdate(false);
2639         }
2640         break;
2641
2642                 /* -------> Set mark off. */
2643         case LFUN_MARK_OFF:
2644         {
2645                 LyXText * lt = bv_->getLyXText();
2646
2647                 beforeChange(lt);
2648                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2649                 lt->selection.cursor = lt->cursor;
2650                 owner_->getLyXFunc()->setMessage(N_("Mark off"));
2651         }
2652         break;
2653
2654                 /* -------> Set mark on. */
2655         case LFUN_MARK_ON:
2656         {
2657                 LyXText * lt = bv_->getLyXText();
2658
2659                 beforeChange(lt);
2660                 lt->selection.mark(true);
2661                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2662                 lt->selection.cursor = lt->cursor;
2663                 owner_->getLyXFunc()->setMessage(N_("Mark on"));
2664         }
2665         break;
2666
2667         case LFUN_BACKSPACE:
2668         {
2669                 LyXText * lt = bv_->getLyXText();
2670
2671                 if (!lt->selection.set()) {
2672                         if (owner_->getIntl()->getTrans().backspace()) {
2673                                 lt->backspace(bv_);
2674                                 lt->selection.cursor = lt->cursor;
2675                                 update(lt,
2676                                        BufferView::SELECT
2677                                        | BufferView::FITCUR
2678                                        | BufferView::CHANGE);
2679                                 // It is possible to make it a lot faster still
2680                                 // just comment out the line below...
2681                                 showCursor();
2682                         }
2683                 } else {
2684                         bv_->cut(false);
2685                 }
2686                 owner_->showState();
2687                 setState();
2688         }
2689         break;
2690
2691         case LFUN_BACKSPACE_SKIP:
2692         {
2693                 // Reverse the effect of LFUN_BREAKPARAGRAPH_SKIP.
2694                 LyXText * lt = bv_->getLyXText();
2695
2696                 LyXCursor cursor = lt->cursor;
2697
2698                 if (!lt->selection.set()) {
2699                         if (cursor.pos() == 0
2700                             && !(cursor.par()->params().spaceTop()
2701                                  == VSpace (VSpace::NONE))) {
2702                                 lt->setParagraph
2703                                         (bv_,
2704                                          cursor.par()->params().lineTop(),
2705                                          cursor.par()->params().lineBottom(),
2706                                          cursor.par()->params().pagebreakTop(),
2707                                          cursor.par()->params().pagebreakBottom(),
2708                                          VSpace(VSpace::NONE), cursor.par()->params().spaceBottom(),
2709                                          cursor.par()->params().spacing(),
2710                                          cursor.par()->params().align(),
2711                                          cursor.par()->params().labelWidthString(), 0);
2712                                 update(lt,
2713                                        BufferView::SELECT
2714                                        | BufferView::FITCUR
2715                                        | BufferView::CHANGE);
2716                         } else {
2717                                 lt->backspace(bv_);
2718                                 lt->selection.cursor = cursor;
2719                                 update(lt,
2720                                        BufferView::SELECT
2721                                        | BufferView::FITCUR
2722                                        | BufferView::CHANGE);
2723                         }
2724                 } else
2725                         bv_->cut(false);
2726         }
2727         break;
2728
2729         case LFUN_BREAKPARAGRAPH:
2730         {
2731                 LyXText * lt = bv_->getLyXText();
2732
2733                 beforeChange(lt);
2734                 lt->breakParagraph(bv_, 0);
2735                 update(lt,
2736                        BufferView::SELECT
2737                        | BufferView::FITCUR
2738                        | BufferView::CHANGE);
2739                 lt->selection.cursor = lt->cursor;
2740                 setState();
2741                 owner_->showState();
2742                 break;
2743         }
2744
2745         case LFUN_BREAKPARAGRAPHKEEPLAYOUT:
2746         {
2747                 LyXText * lt = bv_->getLyXText();
2748
2749                 beforeChange(lt);
2750                 lt->breakParagraph(bv_, 1);
2751                 update(lt,
2752                        BufferView::SELECT
2753                        | BufferView::FITCUR
2754                        | BufferView::CHANGE);
2755                 lt->selection.cursor = lt->cursor;
2756                 setState();
2757                 owner_->showState();
2758                 break;
2759         }
2760
2761         case LFUN_BREAKPARAGRAPH_SKIP:
2762         {
2763                 // When at the beginning of a paragraph, remove
2764                 // indentation and add a "defskip" at the top.
2765                 // Otherwise, do the same as LFUN_BREAKPARAGRAPH.
2766                 LyXText * lt = bv_->getLyXText();
2767
2768                 LyXCursor cursor = lt->cursor;
2769
2770                 beforeChange(lt);
2771                 if (cursor.pos() == 0) {
2772                         if (cursor.par()->params().spaceTop() == VSpace(VSpace::NONE)) {
2773                                 lt->setParagraph
2774                                         (bv_,
2775                                          cursor.par()->params().lineTop(),
2776                                          cursor.par()->params().lineBottom(),
2777                                          cursor.par()->params().pagebreakTop(),
2778                                          cursor.par()->params().pagebreakBottom(),
2779                                          VSpace(VSpace::DEFSKIP), cursor.par()->params().spaceBottom(),
2780                                          cursor.par()->params().spacing(),
2781                                          cursor.par()->params().align(),
2782                                          cursor.par()->params().labelWidthString(), 1);
2783                                 //update(BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
2784                         }
2785                 }
2786                 else {
2787                         lt->breakParagraph(bv_, 0);
2788                         //update(BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
2789                 }
2790
2791                 update(lt,
2792                        BufferView::SELECT
2793                        | BufferView::FITCUR
2794                        | BufferView::CHANGE);
2795                 lt->selection.cursor = cursor;
2796                 setState();
2797                 owner_->showState();
2798         }
2799         break;
2800
2801         case LFUN_PARAGRAPH_SPACING:
2802         {
2803                 LyXText * lt = bv_->getLyXText();
2804
2805                 Paragraph * par = lt->cursor.par();
2806                 Spacing::Space cur_spacing = par->params().spacing().getSpace();
2807                 float cur_value = 1.0;
2808                 if (cur_spacing == Spacing::Other) {
2809                         cur_value = par->params().spacing().getValue();
2810                 }
2811
2812                 istringstream istr(argument.c_str());
2813
2814                 string tmp;
2815                 istr >> tmp;
2816                 Spacing::Space new_spacing = cur_spacing;
2817                 float new_value = cur_value;
2818                 if (tmp.empty()) {
2819                         lyxerr << "Missing argument to `paragraph-spacing'"
2820                                << endl;
2821                 } else if (tmp == "single") {
2822                         new_spacing = Spacing::Single;
2823                 } else if (tmp == "onehalf") {
2824                         new_spacing = Spacing::Onehalf;
2825                 } else if (tmp == "double") {
2826                         new_spacing = Spacing::Double;
2827                 } else if (tmp == "other") {
2828                         new_spacing = Spacing::Other;
2829                         float tmpval = 0.0;
2830                         istr >> tmpval;
2831                         lyxerr << "new_value = " << tmpval << endl;
2832                         if (tmpval != 0.0)
2833                                 new_value = tmpval;
2834                 } else if (tmp == "default") {
2835                         new_spacing = Spacing::Default;
2836                 } else {
2837                         lyxerr << _("Unknown spacing argument: ")
2838                                << argument << endl;
2839                 }
2840                 if (cur_spacing != new_spacing || cur_value != new_value) {
2841                         par->params().spacing(Spacing(new_spacing, new_value));
2842                         lt->redoParagraph(bv_);
2843                         update(lt,
2844                                BufferView::SELECT
2845                                | BufferView::FITCUR
2846                                | BufferView::CHANGE);
2847                 }
2848         }
2849         break;
2850
2851         case LFUN_INSET_TOGGLE:
2852         {
2853                 LyXText * lt = bv_->getLyXText();
2854                 hideCursor();
2855                 beforeChange(lt);
2856                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2857                 lt->toggleInset(bv_);
2858                 update(lt, BufferView::SELECT|BufferView::FITCUR);
2859                 setState();
2860         }
2861                 break;
2862
2863         case LFUN_QUOTE:
2864                 smartQuote();
2865                 break;
2866
2867         case LFUN_HTMLURL:
2868         case LFUN_URL:
2869         {
2870                 InsetCommandParams p;
2871                 if (action == LFUN_HTMLURL)
2872                         p.setCmdName("htmlurl");
2873                 else
2874                         p.setCmdName("url");
2875                 owner_->getDialogs()->createUrl(p.getAsString());
2876         }
2877         break;
2878
2879         case LFUN_INSERT_URL:
2880         {
2881                 InsetCommandParams p;
2882                 p.setFromString(argument);
2883
2884                 InsetUrl * inset = new InsetUrl(p);
2885                 if (!insertInset(inset))
2886                         delete inset;
2887                 else
2888                         updateInset(inset, true);
2889         }
2890         break;
2891
2892         case LFUN_INSET_ERT:
2893                 insertAndEditInset(new InsetERT(buffer_->params));
2894                 break;
2895
2896         case LFUN_INSET_EXTERNAL:
2897                 insertAndEditInset(new InsetExternal);
2898                 break;
2899
2900         case LFUN_INSET_FOOTNOTE:
2901                 insertAndEditInset(new InsetFoot(buffer_->params));
2902                 break;
2903
2904         case LFUN_INSET_MARGINAL:
2905                 insertAndEditInset(new InsetMarginal(buffer_->params));
2906                 break;
2907
2908         case LFUN_INSET_MINIPAGE:
2909                 insertAndEditInset(new InsetMinipage(buffer_->params));
2910                 break;
2911
2912         case LFUN_INSERT_NOTE:
2913                 insertAndEditInset(new InsetNote(buffer_->params));
2914                 break;
2915
2916         case LFUN_INSET_FLOAT:
2917                 // check if the float type exist
2918                 if (floatList.typeExist(argument)) {
2919                         insertAndEditInset(new InsetFloat(buffer_->params,
2920                                                           argument));
2921                 } else {
2922                         lyxerr << "Non-existent float type: "
2923                                << argument << endl;
2924                 }
2925                 break;
2926
2927         case LFUN_INSET_WIDE_FLOAT:
2928                 // check if the float type exist
2929                 if (floatList.typeExist(argument)) {
2930                         InsetFloat * new_inset =
2931                                 new InsetFloat(buffer_->params, argument);
2932                         new_inset->wide(true);
2933                         insertAndEditInset(new_inset);
2934                 } else {
2935                         lyxerr << "Non-existent float type: "
2936                                << argument << endl;
2937                 }
2938                 break;
2939
2940 #if 0
2941         case LFUN_INSET_LIST:
2942                 insertAndEditInset(new InsetList);
2943                 break;
2944
2945         case LFUN_INSET_THEOREM:
2946                 insertAndEditInset(new InsetTheorem);
2947                 break;
2948 #endif
2949
2950         case LFUN_INSET_CAPTION:
2951         {
2952                 // Do we have a locking inset...
2953                 if (bv_->theLockingInset()) {
2954                         lyxerr << "Locking inset code: "
2955                                << static_cast<int>(bv_->theLockingInset()->lyxCode());
2956                         InsetCaption * new_inset =
2957                                 new InsetCaption(buffer_->params);
2958                         new_inset->setOwner(bv_->theLockingInset());
2959                         new_inset->setAutoBreakRows(true);
2960                         new_inset->setDrawFrame(0, InsetText::LOCKED);
2961                         new_inset->setFrameColor(0, LColor::captionframe);
2962                         if (insertInset(new_inset))
2963                                 new_inset->edit(bv_);
2964                         else
2965                                 delete new_inset;
2966                 }
2967         }
2968         break;
2969
2970         case LFUN_INSET_TABULAR:
2971         {
2972                 int r = 2;
2973                 int c = 2;
2974                 if (!argument.empty())
2975                         ::sscanf(argument.c_str(),"%d%d", &r, &c);
2976                 InsetTabular * new_inset =
2977                         new InsetTabular(*buffer_, r, c);
2978                 bool const rtl =
2979                         bv_->getLyXText()->real_current_font.isRightToLeft();
2980                 if (!open_new_inset(new_inset, rtl))
2981                         delete new_inset;
2982         }
2983         break;
2984
2985         // --- lyxserver commands ----------------------------
2986
2987         case LFUN_CHARATCURSOR:
2988         {
2989                 pos_type pos = bv_->getLyXText()->cursor.pos();
2990                 if (pos < bv_->getLyXText()->cursor.par()->size())
2991                         owner_->getLyXFunc()->setMessage(
2992                                 tostr(bv_->getLyXText()->cursor.par()->getChar(pos)));
2993                 else
2994                         owner_->getLyXFunc()->setMessage("EOF");
2995         }
2996         break;
2997
2998         case LFUN_GETXY:
2999                 owner_->getLyXFunc()->setMessage(tostr(bv_->getLyXText()->cursor.x())
3000                                                  + ' '
3001                                                  + tostr(bv_->getLyXText()->cursor.y()));
3002                 break;
3003
3004         case LFUN_SETXY:
3005         {
3006                 int x = 0;
3007                 int y = 0;
3008                 if (::sscanf(argument.c_str(), " %d %d", &x, &y) != 2) {
3009                         lyxerr << "SETXY: Could not parse coordinates in '"
3010                                << argument << std::endl;
3011                 }
3012                 bv_->getLyXText()->setCursorFromCoordinates(bv_, x, y);
3013         }
3014         break;
3015
3016         case LFUN_GETLAYOUT:
3017                 owner_->getLyXFunc()->setMessage(tostr(bv_->getLyXText()->cursor.par()->layout()));
3018                 break;
3019
3020         case LFUN_GETFONT:
3021         {
3022                 LyXFont & font = bv_->getLyXText()->current_font;
3023                 if (font.shape() == LyXFont::ITALIC_SHAPE)
3024                         owner_->getLyXFunc()->setMessage("E");
3025                 else if (font.shape() == LyXFont::SMALLCAPS_SHAPE)
3026                         owner_->getLyXFunc()->setMessage("N");
3027                 else
3028                         owner_->getLyXFunc()->setMessage("0");
3029
3030         }
3031         break;
3032
3033         // --- accented characters ---------------------------
3034
3035         case LFUN_UMLAUT:
3036         case LFUN_CIRCUMFLEX:
3037         case LFUN_GRAVE:
3038         case LFUN_ACUTE:
3039         case LFUN_TILDE:
3040         case LFUN_CEDILLA:
3041         case LFUN_MACRON:
3042         case LFUN_DOT:
3043         case LFUN_UNDERDOT:
3044         case LFUN_UNDERBAR:
3045         case LFUN_CARON:
3046         case LFUN_SPECIAL_CARON:
3047         case LFUN_BREVE:
3048         case LFUN_TIE:
3049         case LFUN_HUNG_UMLAUT:
3050         case LFUN_CIRCLE:
3051         case LFUN_OGONEK:
3052                 if (argument.empty()) {
3053                         // As always...
3054                         owner_->getLyXFunc()->handleKeyFunc(action);
3055                 } else {
3056                         owner_->getLyXFunc()->handleKeyFunc(action);
3057                         owner_->getIntl()->getTrans()
3058                                 .TranslateAndInsert(argument[0], bv_->getLyXText());
3059                         update(bv_->getLyXText(),
3060                                BufferView::SELECT
3061                                | BufferView::FITCUR
3062                                | BufferView::CHANGE);
3063                 }
3064                 break;
3065
3066         case LFUN_MATH_MACRO:
3067                 mathDispatchMathMacro(bv_, argument);
3068                 break;
3069
3070         case LFUN_MATH_DELIM:
3071                 mathDispatchMathDelim(bv_, argument);
3072                 break;
3073
3074         case LFUN_INSERT_MATRIX:
3075                 mathDispatchInsertMatrix(bv_, argument);
3076                 break;
3077
3078         case LFUN_INSERT_MATH:
3079                 mathDispatchInsertMath(bv_, argument);
3080                 break;
3081
3082         case LFUN_MATH_IMPORT_SELECTION: // Imports LaTeX from the X selection
3083                 mathDispatchMathImportSelection(bv_, argument);
3084                 break;
3085
3086         case LFUN_MATH_DISPLAY:          // Open or create a displayed math inset
3087                 mathDispatchMathDisplay(bv_, argument);
3088                 break;
3089
3090         case LFUN_MATH_MODE:             // Open or create an inlined math inset
3091                 mathDispatchMathMode(bv_, argument);
3092                 break;
3093
3094         case LFUN_GREEK:                 // Insert a single greek letter
3095                 mathDispatchGreek(bv_, argument);
3096                 break;
3097
3098         case LFUN_CITATION_INSERT:
3099         {
3100                 InsetCommandParams p;
3101                 p.setFromString(argument);
3102
3103                 InsetCitation * inset = new InsetCitation(p);
3104                 if (!insertInset(inset))
3105                         delete inset;
3106                 else
3107                         updateInset(inset, true);
3108         }
3109         break;
3110
3111         case LFUN_INSERT_BIBTEX:
3112         {
3113                 // ale970405+lasgoutt970425
3114                 // The argument can be up to two tokens separated
3115                 // by a space. The first one is the bibstyle.
3116                 string const db       = token(argument, ' ', 0);
3117                 string const bibstyle = token(argument, ' ', 1);
3118
3119                 InsetCommandParams p("BibTeX", db, bibstyle);
3120                 InsetBibtex * inset = new InsetBibtex(p);
3121
3122                 if (insertInset(inset)) {
3123                         if (argument.empty())
3124                                 inset->edit(bv_);
3125                 } else
3126                         delete inset;
3127         }
3128         break;
3129
3130         // BibTeX data bases
3131         case LFUN_BIBDB_ADD:
3132         {
3133                 InsetBibtex * inset =
3134                         static_cast<InsetBibtex*>(getInsetByCode(Inset::BIBTEX_CODE));
3135                 if (inset) {
3136                         inset->addDatabase(argument);
3137                 }
3138         }
3139         break;
3140
3141         case LFUN_BIBDB_DEL:
3142         {
3143                 InsetBibtex * inset =
3144                         static_cast<InsetBibtex*>(getInsetByCode(Inset::BIBTEX_CODE));
3145                 if (inset) {
3146                         inset->delDatabase(argument);
3147                 }
3148         }
3149         break;
3150
3151         case LFUN_BIBTEX_STYLE:
3152         {
3153                 InsetBibtex * inset =
3154                         static_cast<InsetBibtex*>(getInsetByCode(Inset::BIBTEX_CODE));
3155                 if (inset) {
3156                         inset->setOptions(argument);
3157                 }
3158         }
3159         break;
3160
3161         case LFUN_INDEX_CREATE:
3162         {
3163                 InsetCommandParams p("index");
3164                 if (argument.empty()) {
3165                         string const idxstring(bv_->getLyXText()->getStringToIndex(bv_));
3166                         p.setContents(idxstring);
3167                 } else {
3168                         p.setContents(argument);
3169                 }
3170
3171                 owner_->getDialogs()->createIndex(p.getAsString());
3172         }
3173         break;
3174
3175         case LFUN_INDEX_INSERT:
3176         {
3177                 InsetCommandParams p;
3178                 p.setFromString(argument);
3179                 InsetIndex * inset = new InsetIndex(p);
3180
3181                 if (!insertInset(inset))
3182                         delete inset;
3183                 else
3184                         updateInset(inset, true);
3185         }
3186         break;
3187
3188         case LFUN_INDEX_INSERT_LAST:
3189         {
3190                 string const idxstring(bv_->getLyXText()->getStringToIndex(bv_));
3191                 if (!idxstring.empty()) {
3192                         owner_->message(_("Word `")
3193                                         + idxstring + _(("' indexed.")));
3194                         InsetCommandParams p("index", idxstring);
3195                         InsetIndex * inset = new InsetIndex(p);
3196
3197                         if (!insertInset(inset))
3198                                 delete inset;
3199                         else
3200                                 updateInset(inset, true);
3201                 }
3202         }
3203         break;
3204
3205         case LFUN_INDEX_PRINT:
3206         {
3207                 InsetCommandParams p("printindex");
3208                 Inset * inset = new InsetPrintIndex(p);
3209                 if (!insertInset(inset, tclass.defaultLayoutName()))
3210                         delete inset;
3211         }
3212         break;
3213
3214         case LFUN_PARENTINSERT:
3215         {
3216                 InsetCommandParams p("lyxparent", argument);
3217                 Inset * inset = new InsetParent(p, *buffer_);
3218                 if (!insertInset(inset, tclass.defaultLayoutName()))
3219                         delete inset;
3220         }
3221
3222         break;
3223
3224         case LFUN_CHILD_INSERT:
3225         {
3226                 InsetInclude::Params p;
3227                 p.cparams.setFromString(argument);
3228                 p.masterFilename_ = buffer_->fileName();
3229
3230                 InsetInclude * inset = new InsetInclude(p);
3231                 if (!insertInset(inset))
3232                         delete inset;
3233                 else {
3234                         updateInset(inset, true);
3235                         bv_->owner()->getDialogs()->showInclude(inset);
3236                 }
3237         }
3238         break;
3239
3240         case LFUN_FLOAT_LIST:
3241                 if (floatList.typeExist(argument)) {
3242                         Inset * inset = new InsetFloatList(argument);
3243                         if (!insertInset(inset, tclass.defaultLayoutName()))
3244                                 delete inset;
3245                 } else {
3246                         lyxerr << "Non-existent float type: "
3247                                << argument << endl;
3248                 }
3249                 break;
3250
3251         case LFUN_THESAURUS_ENTRY:
3252         {
3253                 string arg = argument;
3254
3255                 if (arg.empty()) {
3256                         arg = bv_->getLyXText()->selectionAsString(buffer_,
3257                                                                    false);
3258
3259                         // FIXME
3260                         if (arg.size() > 100 || arg.empty()) {
3261                                 // Get word or selection
3262                                 bv_->getLyXText()->selectWordWhenUnderCursor(bv_, LyXText::WHOLE_WORD);
3263                                 arg = bv_->getLyXText()->selectionAsString(buffer_, false);
3264                                 // FIXME: where is getLyXText()->unselect(bv_) ?
3265                         }
3266                 }
3267
3268                 bv_->owner()->getDialogs()->showThesaurus(arg);
3269         }
3270                 break;
3271
3272         case LFUN_SELFINSERT:
3273         {
3274                 if (argument.empty()) break;
3275
3276                 /* Automatically delete the currently selected
3277                  * text and replace it with what is being
3278                  * typed in now. Depends on lyxrc settings
3279                  * "auto_region_delete", which defaults to
3280                  * true (on). */
3281
3282                 LyXText * lt = bv_->getLyXText();
3283
3284                 if (lyxrc.auto_region_delete) {
3285                         if (lt->selection.set()) {
3286                                 lt->cutSelection(bv_, false, false);
3287                                 bv_->update(lt,
3288                                             BufferView::SELECT
3289                                             | BufferView::FITCUR
3290                                             | BufferView::CHANGE);
3291                         }
3292                         workarea().haveSelection(false);
3293                 }
3294
3295                 beforeChange(lt);
3296                 LyXFont const old_font(lt->real_current_font);
3297
3298                 string::const_iterator cit = argument.begin();
3299                 string::const_iterator end = argument.end();
3300                 for (; cit != end; ++cit) {
3301                         owner_->getIntl()->getTrans().TranslateAndInsert(*cit, lt);
3302                 }
3303
3304                 bv_->update(lt,
3305                             BufferView::SELECT
3306                             | BufferView::FITCUR
3307                             | BufferView::CHANGE);
3308
3309                 lt->selection.cursor = lt->cursor;
3310                 moveCursorUpdate(false);
3311
3312                 // real_current_font.number can change so we need to
3313                 // update the minibuffer
3314                 if (old_font != lt->real_current_font)
3315                         owner_->showState();
3316                 //return string();
3317         }
3318         break;
3319
3320         case LFUN_DATE_INSERT:  // jdblair: date-insert cmd
3321         {
3322                 time_t now_time_t = time(NULL);
3323                 struct tm * now_tm = localtime(&now_time_t);
3324                 setlocale(LC_TIME, "");
3325                 string arg;
3326                 if (!argument.empty())
3327                         arg = argument;
3328                 else
3329                         arg = lyxrc.date_insert_format;
3330                 char datetmp[32];
3331                 int const datetmp_len =
3332                         ::strftime(datetmp, 32, arg.c_str(), now_tm);
3333
3334                 LyXText * lt = bv_->getLyXText();
3335
3336                 for (int i = 0; i < datetmp_len; i++) {
3337                         lt->insertChar(bv_, datetmp[i]);
3338                         update(lt,
3339                                BufferView::SELECT
3340                                | BufferView::FITCUR
3341                                | BufferView::CHANGE);
3342                 }
3343
3344                 lt->selection.cursor = lt->cursor;
3345                 moveCursorUpdate(false);
3346         }
3347         break;
3348
3349         case LFUN_UNKNOWN_ACTION:
3350                 owner_->getLyXFunc()->setErrorMessage(N_("Unknown function!"));
3351                 break;
3352
3353         default:
3354                 return false;
3355         } // end of switch
3356
3357         return true;
3358 }
3359
3360
3361 void BufferView::Pimpl::newline()
3362 {
3363         if (available()) {
3364                 LyXText * lt = bv_->getLyXText();
3365                 hideCursor();
3366                 update(lt,
3367                        BufferView::SELECT
3368                        | BufferView::FITCUR);
3369                 lt->insertChar(bv_, Paragraph::META_NEWLINE);
3370                 update(lt,
3371                        BufferView::SELECT
3372                        | BufferView::FITCUR
3373                        | BufferView::CHANGE);
3374         }
3375 }
3376
3377
3378 void BufferView::Pimpl::hfill()
3379 {
3380         if (available()) {
3381                 LyXText * lt = bv_->getLyXText();
3382                 hideCursor();
3383                 update(lt,
3384                        BufferView::SELECT
3385                        | BufferView::FITCUR);
3386                 lt->insertChar(bv_, Paragraph::META_HFILL);
3387                 update(lt,
3388                        BufferView::SELECT
3389                        | BufferView::FITCUR
3390                        | BufferView::CHANGE);
3391         }
3392 }
3393
3394
3395 void BufferView::Pimpl::specialChar(InsetSpecialChar::Kind kind)
3396 {
3397         if (available()) {
3398                 LyXText * lt = bv_->getLyXText();
3399
3400                 hideCursor();
3401                 update(lt, BufferView::SELECT|BufferView::FITCUR);
3402                 InsetSpecialChar * new_inset =
3403                         new InsetSpecialChar(kind);
3404                 if (!insertInset(new_inset))
3405                         delete new_inset;
3406                 else
3407                         updateInset(new_inset, true);
3408         }
3409 }
3410
3411
3412 void BufferView::Pimpl::smartQuote()
3413 {
3414         LyXText const * lt = bv_->getLyXText();
3415         Paragraph const * par = lt->cursor.par();
3416         pos_type pos = lt->cursor.pos();
3417         char c;
3418
3419         if (!pos
3420             || (par->isInset(pos - 1)
3421                 && par->getInset(pos - 1)->isSpace()))
3422                 c = ' ';
3423         else
3424                 c = par->getChar(pos - 1);
3425
3426
3427         hideCursor();
3428
3429         LyXLayout const & style =
3430                 textclasslist[bv_->buffer()->params.textclass][par->layout()];
3431
3432         if (style.pass_thru ||
3433                 (!insertInset(new InsetQuotes(c, bv_->buffer()->params))))
3434                 bv_->owner()->getLyXFunc()->dispatch(LFUN_SELFINSERT, "\"");
3435 }
3436
3437
3438 void BufferView::Pimpl::insertAndEditInset(Inset * inset)
3439 {
3440         if (insertInset(inset))
3441                 inset->edit(bv_);
3442         else
3443                 delete inset;
3444 }
3445
3446
3447 // Open and lock an updatable inset
3448 bool BufferView::Pimpl::open_new_inset(UpdatableInset * new_inset, bool behind)
3449 {
3450         LyXText * lt = bv_->getLyXText();
3451
3452         beforeChange(lt);
3453         finishUndo();
3454         if (!insertInset(new_inset)) {
3455                 delete new_inset;
3456                 return false;
3457         }
3458         new_inset->edit(bv_, !behind);
3459         return true;
3460 }
3461
3462
3463 bool BufferView::Pimpl::insertInset(Inset * inset, string const & lout)
3464 {
3465         // if we are in a locking inset we should try to insert the
3466         // inset there otherwise this is a illegal function now
3467         if (bv_->theLockingInset()) {
3468                 if (bv_->theLockingInset()->insetAllowed(inset))
3469                     return bv_->theLockingInset()->insertInset(bv_, inset);
3470                 return false;
3471         }
3472
3473         // not quite sure if we want this...
3474         setCursorParUndo(bv_);
3475         freezeUndo();
3476
3477         beforeChange(bv_->text);
3478         if (!lout.empty()) {
3479                 update(bv_->text, BufferView::SELECT|BufferView::FITCUR);
3480                 bv_->text->breakParagraph(bv_);
3481                 update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
3482
3483                 if (bv_->text->cursor.par()->size()) {
3484                         bv_->text->cursorLeft(bv_);
3485
3486                         bv_->text->breakParagraph(bv_);
3487                         update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
3488                 }
3489
3490                 string lres = lout;
3491                 LyXTextClass const & tclass =
3492                         textclasslist[buffer_->params.textclass];
3493                 bool hasLayout = tclass.hasLayout(lres);
3494                 string lay = tclass.defaultLayoutName();
3495
3496                 if (hasLayout != false) {
3497                         // layout found
3498                         lay = lres;
3499                 } else {
3500                         // layout not fount using default
3501                         lay = tclass.defaultLayoutName();
3502                 }
3503
3504                 bv_->text->setLayout(bv_, lay);
3505
3506                 bv_->text->setParagraph(bv_, 0, 0,
3507                                    0, 0,
3508                                    VSpace(VSpace::NONE), VSpace(VSpace::NONE),
3509                                    Spacing(),
3510                                    LYX_ALIGN_LAYOUT,
3511                                    string(),
3512                                    0);
3513                 update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
3514         }
3515
3516         bv_->text->insertInset(bv_, inset);
3517         update(bv_->text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
3518
3519         unFreezeUndo();
3520         return true;
3521 }
3522
3523
3524 void BufferView::Pimpl::updateInset(Inset * inset, bool mark_dirty)
3525 {
3526         if (!inset || !available())
3527                 return;
3528
3529         // first check for locking insets
3530         if (bv_->theLockingInset()) {
3531                 if (bv_->theLockingInset() == inset) {
3532                         if (bv_->text->updateInset(bv_, inset)) {
3533                                 update();
3534                                 if (mark_dirty) {
3535                                         buffer_->markDirty();
3536                                 }
3537                                 updateScrollbar();
3538                                 return;
3539                         }
3540                 } else if (bv_->theLockingInset()->updateInsetInInset(bv_, inset)) {
3541                         if (bv_->text->updateInset(bv_,  bv_->theLockingInset())) {
3542                                 update();
3543                                 if (mark_dirty) {
3544                                         buffer_->markDirty();
3545                                 }
3546                                 updateScrollbar();
3547                                 return;
3548                         }
3549                 }
3550         }
3551
3552         // then check if the inset is a top_level inset (has no owner)
3553         // if yes do the update as always otherwise we have to update the
3554         // toplevel inset where this inset is inside
3555         Inset * tl_inset = inset;
3556         while(tl_inset->owner())
3557                 tl_inset = tl_inset->owner();
3558         hideCursor();
3559         if (tl_inset == inset) {
3560                 update(bv_->text, BufferView::UPDATE);
3561                 if (bv_->text->updateInset(bv_, inset)) {
3562                         if (mark_dirty) {
3563                                 update(bv_->text,
3564                                        BufferView::SELECT
3565                                        | BufferView::FITCUR
3566                                        | BufferView::CHANGE);
3567                         } else {
3568                                 update(bv_->text, SELECT);
3569                         }
3570                         return;
3571                 }
3572         } else if (static_cast<UpdatableInset *>(tl_inset)
3573                            ->updateInsetInInset(bv_, inset))
3574         {
3575                         if (bv_->text->updateInset(bv_,  tl_inset)) {
3576                                 update();
3577                                 updateScrollbar();
3578                         }
3579         }
3580 }
3581
3582
3583 void BufferView::Pimpl::gotoInset(vector<Inset::Code> const & codes,
3584                                   bool same_content)
3585 {
3586         if (!available()) return;
3587
3588         hideCursor();
3589         beforeChange(bv_->text);
3590         update(bv_->text, BufferView::SELECT|BufferView::FITCUR);
3591
3592         LyXCursor const & cursor = bv_->text->cursor;
3593
3594         string contents;
3595         if (same_content &&
3596             cursor.par()->isInset(cursor.pos())) {
3597                 Inset const * inset = cursor.par()->getInset(cursor.pos());
3598                 if (find(codes.begin(), codes.end(), inset->lyxCode())
3599                     != codes.end())
3600                         contents =
3601                                 static_cast<InsetCommand const *>(inset)->getContents();
3602         }
3603
3604
3605         if (!bv_->text->gotoNextInset(bv_, codes, contents)) {
3606                 if (bv_->text->cursor.pos()
3607                     || bv_->text->cursor.par() != bv_->text->ownerParagraph()) {
3608                         LyXCursor tmp = bv_->text->cursor;
3609                         bv_->text->cursor.par(bv_->text->ownerParagraph());
3610                         bv_->text->cursor.pos(0);
3611                         if (!bv_->text->gotoNextInset(bv_, codes, contents)) {
3612                                 bv_->text->cursor = tmp;
3613                                 bv_->owner()->message(_("No more insets"));
3614                         }
3615                 } else {
3616                         bv_->owner()->message(_("No more insets"));
3617                 }
3618         }
3619         update(bv_->text, BufferView::SELECT|BufferView::FITCUR);
3620         bv_->text->selection.cursor = bv_->text->cursor;
3621 }
3622
3623
3624 void BufferView::Pimpl::gotoInset(Inset::Code code, bool same_content)
3625 {
3626         gotoInset(vector<Inset::Code>(1, code), same_content);
3627 }