# HG changeset patch
# User Daniel J Sebald <daniel.sebald@ieee.org>
# Date 1368211757 18000
# Node ID 37310910e6524b5c69c0912332ea16e9cc719738
# Parent  8d32a887754a4bb448b10bd359e7a7335adb66b4
New marker class for modified file breakpoint and position maintenance.

* file-editor-tab.cc
(file_editor_tab::file_editor_tab): Use class prefix marker:: on all
marker enumeration uses. New markerDefine for unsure_breakpoint and
unsure_debugger_position.
(file_editor_tab::~file_editor_tab): Signal to remove all breakpoint and
position objects.
(file_editor_tab::message_cannot_breakpoint_changed_file) New message.
(file_editor_tab::handle_margin_clicked) Emit signal to remove marker via
editor line number. Display error message for modified file.
(file_editor_tab::handle_request_add_breakpoint): Rename. Do not add 1 to
line number.
(file_editor_tab::handle_request_remove_breakpoint): Rename. Do not add 1
to line number.
(file_editor_tab::toggle_breakpoint): Change "line" to "editor_linenr".
Display error message for modified file.  Add 1 to editor_linenr.
(file_editor_tab::load_file): Add prototype code for getting "dbstatus".
(file_editor_tab::handle_octave_result): New method. Prototype code for
processing Octave result.
(file_editor_tab::save_file): Record list of breakpoints. Save file. Restore
breakpoints if instructed after save.
(file_editor_tab::notice_settings): Configure _breakpoint_filesave_behavior and
uncertain marker icon set.
(file_editor_tab::insert_debugger_pointer): Compute best guess at debugger
position in modified file. Connect signals/slots to pointer marker.
(file_editor_tab::delete_debugger_pointer): Remove direct editor marker
setting. Emit signal to marker.
(file_editor_tab::do_breakpoint_marker): If breakpoint doesn't already exist,
construct marker object, connect signals. If exists, emit delete signal to
breakpoint marker.

* file-editor-tab.h (file_editor_tab : public QWidget): Multitude of signal
and slot declarations as described above. Add _breakpoint_filesave_behavior
setting member variable.
(header): Move editor_markers enumeration to marker.h. Include "marker.h".

* marker.cc, marker.h (marker : public QObject): New files. New marker class
comprised of brief slots to interact with QsciScintalla object and brief signal
for Octave removal request. New unsure_breakpoint and unsure_debugger_position
marker enumeration definitions.

* arrow_right_gray.png, redled_gray.png: New marker icons.

* module.mk: Add marker.h, marker.cc, arrow_right_gray.png, redled_gray.png.

* resource.qrc: Add redled_gray.png, arrow_right_gray.png.

* settings-dialog.cc (settings_dialog::settings_dialog): Show options, icons
and settings for debugger/breakpoint_filesave_behavior and
debugger/uncertain_icon_set.
(settings_dialog::write_changed_settings): Write settings for
debugger/breakpoint_filesave_behavior and debugger/uncertain_icon_set.

* settings-dialog.ui: New "Debugger" tab with two group boxes.

diff --git a/libgui/src/icons/arrow_right_gray.png b/libgui/src/icons/arrow_right_gray.png
new file mode 100644
index 0000000000000000000000000000000000000000..9f50e5ef91772c26c407ed5cecdb734f5a757a19
GIT binary patch
diff --git a/libgui/src/icons/redled_gray.png b/libgui/src/icons/redled_gray.png
new file mode 100644
index 0000000000000000000000000000000000000000..94d5ccd566322301ece103d4571d2ebc669cc81e
GIT binary patch
diff --git a/libgui/src/m-editor/file-editor-tab.cc b/libgui/src/m-editor/file-editor-tab.cc
--- a/libgui/src/m-editor/file-editor-tab.cc
+++ b/libgui/src/m-editor/file-editor-tab.cc
@@ -44,9 +44,16 @@
 #include <QVBoxLayout>
 #include <QInputDialog>
 #include <QPrintDialog>
+#include <QStyle>
+#include <QTextBlock>
+#include <QLabel>
+#include <QCheckBox>
+#include <QDialogButtonBox>
+#include <QPushButton>
 
 #include "file-editor-tab.h"
 #include "file-editor.h"
+#include "marker.h"
 
 #include "cmd-edit.h"
 
@@ -79,11 +86,15 @@
   // symbols
   _edit_area->setMarginType (1, QsciScintilla::SymbolMargin);
   _edit_area->setMarginSensitivity (1, true);
-  _edit_area->markerDefine (QsciScintilla::RightTriangle, bookmark);
+  _edit_area->markerDefine (QsciScintilla::RightTriangle, marker::bookmark);
   _edit_area->markerDefine (QPixmap (":/actions/icons/redled.png"),
-                            breakpoint);
+                            marker::breakpoint);
+  _edit_area->markerDefine (QPixmap (":/actions/icons/redled.png"),
+                            marker::unsure_breakpoint);
   _edit_area->markerDefine (QPixmap (":/actions/icons/arrow_right.png"),
-                            debugger_position);
+                            marker::debugger_position);
+  _edit_area->markerDefine (QPixmap (":/actions/icons/arrow_right.png"),
+                            marker::unsure_debugger_position);
 
   connect (_edit_area, SIGNAL (marginClicked (int, int,
                                               Qt::KeyboardModifiers)),
@@ -136,6 +147,10 @@
 
 file_editor_tab::~file_editor_tab (void)
 {
+  // Tell all connected markers to self-destruct.
+  emit remove_all_breakpoints ();
+  emit remove_all_positions ();
+
   // Destroy items attached to _edit_area.
   QsciLexer *lexer = _edit_area->lexer ();
   if (lexer)
@@ -185,26 +200,47 @@
 }
 
 void
-file_editor_tab::handle_margin_clicked (int margin, int line,
-                                       Qt::KeyboardModifiers state)
+file_editor_tab::message_cannot_breakpoint_changed_file (void)
+{
+  // Cannot create a breakpoint when the file is modified
+  // because the line number the editor is providing might
+  // not match what Octave core is interpretting in the
+  // file on disk.
+  QMessageBox* msgBox = new QMessageBox (QMessageBox::Critical,
+                                         tr ("Octave Editor"),
+                                         tr ("Cannot add breakpoint to modified file."),
+                                         QMessageBox::Ok, 0);
+  msgBox->setWindowModality (Qt::ApplicationModal);
+  msgBox->exec ();
+  delete msgBox;
+}
+
+void
+file_editor_tab::handle_margin_clicked (int margin, int editor_linenr,
+                                        Qt::KeyboardModifiers state)
 {
   if (margin == 1)
     {
-      unsigned int markers_mask = _edit_area->markersAtLine (line);
+      unsigned int markers_mask = _edit_area->markersAtLine (editor_linenr);
 
       if (state & Qt::ControlModifier)
         {
-          if (markers_mask && (1 << bookmark))
-            _edit_area->markerDelete (line, bookmark);
+          if (markers_mask && (1 << marker::bookmark))
+            _edit_area->markerDelete (editor_linenr, marker::bookmark);
           else
-            _edit_area->markerAdd (line, bookmark);
+            _edit_area->markerAdd (editor_linenr, marker::bookmark);
         }
       else
         {
-          if (markers_mask && (1 << breakpoint))
-            request_remove_breakpoint (line);
+          if (markers_mask && (1 << marker::breakpoint))
+            emit request_remove_breakpoint_via_editor_linenr (editor_linenr);
           else
-            request_add_breakpoint (line);
+            {
+              if (_edit_area->isModified ())
+                message_cannot_breakpoint_changed_file ();
+              else
+                handle_request_add_breakpoint (editor_linenr + 1);
+            }
         }
     }
 }
@@ -402,10 +438,10 @@
   int line, cur;
   _edit_area->getCursorPosition (&line, &cur);
 
-  if (_edit_area->markersAtLine (line) && (1 << bookmark))
-    _edit_area->markerDelete (line, bookmark);
+  if (_edit_area->markersAtLine (line) && (1 << marker::bookmark))
+    _edit_area->markerDelete (line, marker::bookmark);
   else
-    _edit_area->markerAdd (line, bookmark);
+    _edit_area->markerAdd (line, marker::bookmark);
 }
 
 void
@@ -417,10 +453,10 @@
   int line, cur;
   _edit_area->getCursorPosition (&line, &cur);
 
-  if (_edit_area->markersAtLine (line) && (1 << bookmark))
+  if (_edit_area->markersAtLine (line) && (1 << marker::bookmark))
     line++; // we have a breakpoint here, so start search from next line
 
-  int nextline = _edit_area->markerFindNext (line, (1 << bookmark));
+  int nextline = _edit_area->markerFindNext (line, (1 << marker::bookmark));
 
   _edit_area->setCursorPosition (nextline, 0);
 }
@@ -434,10 +470,10 @@
   int line, cur;
   _edit_area->getCursorPosition (&line, &cur);
 
-  if (_edit_area->markersAtLine (line) && (1 << bookmark))
+  if (_edit_area->markersAtLine (line) && (1 << marker::bookmark))
     line--; // we have a breakpoint here, so start search from prev line
 
-  int prevline = _edit_area->markerFindPrevious (line, (1 << bookmark));
+  int prevline = _edit_area->markerFindPrevious (line, (1 << marker::bookmark));
 
   _edit_area->setCursorPosition (prevline, 0);
 }
@@ -448,7 +484,7 @@
   if (ID != this)
     return;
 
-  _edit_area->markerDeleteAll (bookmark);
+  _edit_area->markerDeleteAll (marker::bookmark);
 }
 
 bool
@@ -543,7 +579,7 @@
 }
 
 void
-file_editor_tab::request_add_breakpoint (int line)
+file_editor_tab::handle_request_add_breakpoint (int line)
 {
   QFileInfo file_info (_file_name);
   QString dir = file_info.absolutePath ();
@@ -552,14 +588,14 @@
   // We have to cut off the suffix, because octave appends it.
   function_name.chop (file_info.suffix ().length () + 1);
 
-  bp_info info (_file_name, dir, function_name, line+1);
+  bp_info info (_file_name, dir, function_name, line);
 
   octave_link::post_event
     (this, &file_editor_tab::add_breakpoint_callback, info);
 }
 
 void
-file_editor_tab::request_remove_breakpoint (int line)
+file_editor_tab::handle_request_remove_breakpoint (int line)
 {
   QFileInfo file_info (_file_name);
   QString dir = file_info.absolutePath ();
@@ -568,7 +604,7 @@
   // We have to cut off the suffix, because octave appends it.
   function_name.chop (file_info.suffix ().length () + 1);
 
-  bp_info info (_file_name, dir, function_name, line+1);
+  bp_info info (_file_name, dir, function_name, line);
 
   octave_link::post_event
     (this, &file_editor_tab::remove_breakpoint_callback, info);
@@ -580,13 +616,18 @@
   if (ID != this)
     return;
 
-  int line, cur;
-  _edit_area->getCursorPosition (&line, &cur);
+  int editor_linenr, cur;
+  _edit_area->getCursorPosition (&editor_linenr, &cur);
 
-  if (_edit_area->markersAtLine (line) && (1 << breakpoint))
-    request_remove_breakpoint (line);
+  if (_edit_area->markersAtLine (editor_linenr) && (1 << marker::breakpoint))
+    request_remove_breakpoint_via_editor_linenr (editor_linenr);
   else
-    request_add_breakpoint (line);
+    {
+      if (_edit_area->isModified ())
+        message_cannot_breakpoint_changed_file ();
+      else
+        handle_request_add_breakpoint (editor_linenr + 1);
+    }
 }
 
 void
@@ -598,10 +639,10 @@
   int line, cur;
   _edit_area->getCursorPosition (&line, &cur);
 
-  if (_edit_area->markersAtLine (line) && (1 << breakpoint))
+  if (_edit_area->markersAtLine (line) && (1 << marker::breakpoint))
     line++; // we have a breakpoint here, so start search from next line
 
-  int nextline = _edit_area->markerFindNext (line, (1 << breakpoint));
+  int nextline = _edit_area->markerFindNext (line, (1 << marker::breakpoint));
 
   _edit_area->setCursorPosition (nextline, 0);
 }
@@ -615,10 +656,10 @@
   int line, cur, prevline;
   _edit_area->getCursorPosition (&line, &cur);
 
-  if (_edit_area->markersAtLine (line) && (1 << breakpoint))
+  if (_edit_area->markersAtLine (line) && (1 << marker::breakpoint))
     line--; // we have a breakpoint here, so start search from prev line
 
-  prevline = _edit_area->markerFindPrevious (line, (1 << breakpoint));
+  prevline = _edit_area->markerFindPrevious (line, (1 << marker::breakpoint));
 
   _edit_area->setCursorPosition (prevline, 0);
 }
@@ -872,10 +913,55 @@
   set_file_name (fileName);
   update_window_title (false); // window title (no modification)
   _edit_area->setModified (false); // loaded file is not modified yet
+ 
+  // TODO: (BREAKPOINTS) At this point it would be nice to put any set
+  // breakpoints on the margin.  In order to do this, somehow the
+  // "dbstatus" command needs to be accessed.  All it would require is a
+  // routine that does "res = feval("dbstatus") and signals that result
+  // to some slot.
+  //
+  // See patch #8016 for a general way to get Octave results from
+  // commands processed in the background.
+
+/*
+  connect (octave_link, SIGNAL (fileSelected (QObject *, const QString&, const octave_value_list&)),
+           this, SLOT (handle_feval_result (QObject *, const QString&, const octave_value_list&)));
+  connect (this, SIGNAL (evaluate_octave_command (const QString&)),
+           octave_link, SLOT (queue_octave_command (const QString&)));
+*/
+
+  emit evaluate_octave_command ("dbstatus");
 
   return QString ();
 }
 
+// TODO: See patch #8016 for a general way to get Octave results from
+// commands processed in the background, e.g., dbstatus.
+void
+file_editor_tab::handle_octave_result (QObject *requester, QString& command,
+                                       octave_value_list &result)
+{
+  // Check if this object initiated the command.
+  if (requester == this)
+    {
+      if (command == "dbstatus")
+        {
+          fprintf (stderr, "Should be installing breakpoints in this file.");
+/*
+octave:1> result = dbstatus
+result =
+
+  0x1 struct array containing the fields:
+
+    name
+    file
+    line
+*/
+          // Check for results that match "file".
+        }
+    }
+}
+
 void
 file_editor_tab::new_file (const QString &commands)
 {
@@ -924,6 +1010,15 @@
       return;
     }
 
+  // Get a list of all the breakpoint line numbers.
+  QIntList list;
+  emit report_editor_linenr (list);
+  if (! list.isEmpty ())
+    {
+      // At least one breakpoint is present.  Get rid of breakpoints.
+      remove_all_breakpoints (this);
+    }
+
   // save the contents into the file
   QTextStream out (&file);
   QApplication::setOverrideCursor (Qt::WaitCursor);
@@ -945,6 +1040,87 @@
       emit tab_remove_request ();
       return;  // Don't touch member variables after removal
     }
+
+  // Attempt to restore the breakpoints if that is desired.
+  if (! list.isEmpty ())
+    {
+      bool restore_breakpoints;
+      if (_breakpoint_filesave_behavior == "RESTORE")
+        restore_breakpoints = true;
+      else if (_breakpoint_filesave_behavior == "DISCARD")
+        restore_breakpoints = false;
+      else
+        {
+          QDialog* dlgBox = new QDialog ();
+
+          QStyle *mbstyle = dlgBox->style ();
+          QIcon tmpIcon = mbstyle->standardIcon (QStyle::SP_MessageBoxQuestion,
+                                                 0, dlgBox);
+          int iconSize = mbstyle->pixelMetric(QStyle::PM_MessageBoxIconSize,
+                                              0, dlgBox);
+          QLabel *questImage = new QLabel ();
+          questImage->setPixmap (tmpIcon.pixmap (iconSize, iconSize));
+          QLabel *questText = new QLabel ("Would you like to restore adjusted breakpoints?");
+          QHBoxLayout *horizontalLayout = new QHBoxLayout;
+          horizontalLayout->addWidget (questImage);
+          horizontalLayout->addWidget (questText);
+
+          QCheckBox *checkBox = new QCheckBox ("Don't ask again.");
+          checkBox->setCheckState (Qt::Unchecked);
+
+          QDialogButtonBox *buttonBox = new QDialogButtonBox ();
+          QPushButton *noButton = buttonBox->addButton (QDialogButtonBox::No);
+          noButton->setAutoDefault (false);
+          buttonBox->addButton (QDialogButtonBox::Yes);
+          QHBoxLayout *buttonsLayout = new QHBoxLayout;
+          buttonsLayout->addStretch (1);
+          buttonsLayout->addWidget (buttonBox);
+
+          QVBoxLayout *mainLayout = new QVBoxLayout;
+          mainLayout->addLayout (horizontalLayout);
+          mainLayout->addSpacing(12);
+          mainLayout->addWidget (checkBox);
+          mainLayout->addSpacing(12);
+          mainLayout->addLayout (buttonsLayout);
+
+          dlgBox->setLayout (mainLayout);
+
+          connect(buttonBox, SIGNAL(accepted()), dlgBox, SLOT(accept()));
+          connect(buttonBox, SIGNAL(rejected()), dlgBox, SLOT(reject()));
+          dlgBox->setWindowModality (Qt::NonModal);
+          dlgBox->exec ();
+
+          restore_breakpoints = (dlgBox->result () == QDialog::Accepted);
+
+          if (checkBox->checkState () == Qt::Checked)
+            {
+              // User no longer wants to be asked so save the setting for
+              // this object...
+              if (restore_breakpoints)
+                _breakpoint_filesave_behavior = "RESTORE";
+              else
+                _breakpoint_filesave_behavior = "DISCARD";
+
+              // ...and on disc (and Preferences...)
+              QSettings *settings = resource_manager::get_settings ();
+              if (settings)
+                {
+                  settings->setValue ("debugger/breakpoint_filesave_behavior",
+                                      _breakpoint_filesave_behavior);
+                  settings->sync ();
+                }
+            }
+
+          delete dlgBox;
+
+        }
+
+      if (restore_breakpoints)
+        {
+          for (int i = 0; i < list.length (); i++)
+            handle_request_add_breakpoint (list.value (i) + 1);
+        }
+    }
 }
 
 void
@@ -1153,6 +1329,40 @@
 
   _long_title = settings->value ("editor/longWindowTitle", false).toBool ();
 
+  _breakpoint_filesave_behavior = settings->value ("debugger/breakpoint_filesave_behavior", "ASK").
+                                                    toString ();
+
+  QVariant uis = settings->value ("debugger/uncertain_icon_set", "SELECTED");
+  if (uis == "CUSTOM")
+    {
+      _edit_area->markerDefine (QPixmap (":/actions/icons/redled_gray.png"),
+                                marker::unsure_breakpoint);
+      _edit_area->markerDefine (QPixmap (":/actions/icons/arrow_right_gray.png"),
+                                marker::unsure_debugger_position);
+    }
+  else if (uis == "DISABLED")
+    {
+      QPixmap tmpPixmap = QPixmap (":/actions/icons/redled.png");
+      QIcon tmpIcon = QIcon (tmpPixmap);
+      _edit_area->markerDefine (tmpIcon.pixmap (tmpPixmap.size (), QIcon::Disabled),
+                                marker::unsure_breakpoint);
+      tmpPixmap = QPixmap (":/actions/icons/arrow_right.png");
+      tmpIcon = QIcon (tmpPixmap);
+      _edit_area->markerDefine (tmpIcon.pixmap (tmpPixmap.size (), QIcon::Disabled),
+                                marker::unsure_debugger_position);
+    }
+  else
+    {
+      QPixmap tmpPixmap = QPixmap (":/actions/icons/redled.png");
+      QIcon tmpIcon = QIcon (tmpPixmap);
+      _edit_area->markerDefine (tmpIcon.pixmap (tmpPixmap.size (), QIcon::Selected),
+                                marker::unsure_breakpoint);
+      tmpPixmap = QPixmap (":/actions/icons/arrow_right.png");
+      tmpIcon = QIcon (tmpPixmap);
+      _edit_area->markerDefine (tmpIcon.pixmap (tmpPixmap.size (), QIcon::Selected),
+                                marker::unsure_debugger_position);
+    }
+
   update_window_title (false);
 }
 
@@ -1247,7 +1457,54 @@
 
   if (line > 0)
     {
-      _edit_area->markerAdd (line-1, debugger_position);
+      marker *dp;
+
+      if (_edit_area->isModified ())
+        {
+          // The best that can be done if the editor contents has been
+          // modified is to see if there is a match with the original
+          // line number of any existing breakpoints.  We can put a normal
+          // debugger pointer at that breakpoint position.  Otherwise, it
+          // isn't certain whether the original line number and current line
+          // number match.
+          int editor_linenr = -1;
+          emit find_translated_line_number (line, editor_linenr);
+          if (editor_linenr != -1)
+            {
+              // Match with an existing breakpoint.
+              dp = new marker (_edit_area, line,
+                               marker::debugger_position, editor_linenr);
+            }
+          else
+            {
+              int original_linenr = -1;
+              editor_linenr = -1;
+              emit find_linenr_just_before (line, original_linenr, editor_linenr);
+              if (original_linenr >= 0)
+                {
+                  // Make a guess by using an offset from the breakpoint.
+                  int linenr_guess = editor_linenr + line - original_linenr;
+                  dp = new marker (_edit_area, line,
+                                   marker::unsure_debugger_position,
+                                   linenr_guess);
+                }
+              else
+                {
+                  // Can't make a very good guess, so just use the debugger
+                  // line number.
+                  dp = new marker (_edit_area, line,
+                                   marker::unsure_debugger_position);
+                }
+            }
+        }
+      else
+        dp = new marker (_edit_area, line, marker::debugger_position);
+
+      connect (this, SIGNAL (remove_position_via_debugger_linenr (int)),
+               dp,   SLOT (handle_remove_via_original_linenr (int)));
+      connect (this, SIGNAL (remove_all_positions (void)),
+               dp,   SLOT (handle_remove (void)));
+
       center_current_line ();
     }
 }
@@ -1259,7 +1516,7 @@
     return;
 
   if (line > 0)
-    _edit_area->markerDelete (line-1, debugger_position);
+    emit remove_position_via_debugger_linenr (line);
 }
 
 void
@@ -1271,9 +1528,35 @@
   if (line > 0)
     {
       if (insert)
-        _edit_area->markerAdd (line-1, breakpoint);
+        {
+          int editor_linenr = -1;
+
+          // If comes back indicating a modified editor line number
+          // then there is already a breakpoint marker associated
+          // with this debugger line.
+          emit find_translated_line_number (line, editor_linenr);
+
+          if (editor_linenr == -1)
+            {
+              marker *bp = new marker (_edit_area, line, marker::breakpoint);
+              connect (this, SIGNAL (remove_breakpoint_via_debugger_linenr (int)),
+                       bp,   SLOT (handle_remove_via_original_linenr (int)));
+              connect (this, SIGNAL (request_remove_breakpoint_via_editor_linenr (int)),
+                       bp,   SLOT (handle_request_remove_via_editor_linenr (int)));
+              connect (this, SIGNAL (remove_all_breakpoints (void)),
+                       bp,   SLOT (handle_remove (void)));
+              connect (this, SIGNAL (find_translated_line_number (int, int&)),
+                       bp,   SLOT (handle_find_translation (int, int&)));
+              connect (this, SIGNAL (find_linenr_just_before (int, int&, int&)),
+                       bp,   SLOT (handle_find_just_before (int, int&, int&)));
+              connect (this, SIGNAL (report_editor_linenr (QIntList&)),
+                       bp,   SLOT (handle_report_editor_linenr (QIntList&)));
+              connect (bp,   SIGNAL (request_remove (int)),
+                       this, SLOT (handle_request_remove_breakpoint (int)));
+            }
+        }
       else
-        _edit_area->markerDelete (line-1, breakpoint);
+        emit remove_breakpoint_via_debugger_linenr (line);
     }
 }
 
diff --git a/libgui/src/m-editor/file-editor-tab.h b/libgui/src/m-editor/file-editor-tab.h
--- a/libgui/src/m-editor/file-editor-tab.h
+++ b/libgui/src/m-editor/file-editor-tab.h
@@ -28,10 +28,20 @@
 #include <QFileSystemWatcher>
 #include <QSettings>
 
+// FIXME -- we should not be including config.h in header files.
+// Only needed for octave_value_list type.
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#include "ov.h"
+#endif
+
 #include <Qsci/qsciscintilla.h>
 
 #include "find-dialog.h"
 
+#include "marker.h" /* Only needed for typedef of "QIntList", which may be
+                       typedefed elsewhere.  Could use common location. */
+
 class file_editor;
 
 class file_editor_tab : public QWidget
@@ -102,6 +112,11 @@
 
   void file_has_changed (const QString& fileName);
 
+  void handle_request_add_breakpoint (int line);
+  void handle_request_remove_breakpoint (int line);
+
+  void handle_octave_result (QObject *requester, QString& command, octave_value_list &result);
+
 signals:
 
   void file_name_changed (const QString& fileName, const QString& toolTip);
@@ -112,6 +127,18 @@
   void editor_check_conflict_save (const QString& saveFileName,
                                    bool remove_on_success);
   void process_octave_code (const QString& command);
+  void remove_breakpoint_via_debugger_linenr (int debugger_linenr);
+  void request_remove_breakpoint_via_editor_linenr (int editor_linenr);
+  void remove_all_breakpoints (void);
+  void find_translated_line_number (int original_linenr, int& translated_linenr);
+  void find_linenr_just_before (int linenr, int& original_linenr, int& editor_linenr);
+  void report_editor_linenr (QIntList& int_list);
+  void remove_position_via_debugger_linenr (int debugger_linenr);
+  void remove_all_positions (void);
+  // TODO: The following is similar to "process_octave_code" signal.  However,
+  // currently that signal is connected to something that simply focuses a
+  // window and not actually communicate with Octave.
+  void evaluate_octave_command (const QString& command);
 
 protected:
 
@@ -139,13 +166,6 @@
 
 private:
 
-  enum editor_markers
-    {
-      bookmark,
-      breakpoint,
-      debugger_position
-    };
-
   struct bp_info
   {
     bp_info (const QString& f, const QString& d, const QString& fn, int l)
@@ -163,9 +183,9 @@
   void save_file_as (bool remove_on_success = false);
   void message_duplicate_file_name (const QString& fileName);
 
+  void message_cannot_breakpoint_changed_file (void);
+
   void update_lexer ();
-  void request_add_breakpoint (int line);
-  void request_remove_breakpoint (int line);
 
   int check_file_modified ();
   void do_comment_selected_text (bool comment);
@@ -187,6 +207,8 @@
   bool _long_title;
   bool _copy_available;
 
+  QString _breakpoint_filesave_behavior;
+
   QFileSystemWatcher _file_system_watcher;
 
   find_dialog *_find_dialog;
diff --git a/libgui/src/m-editor/marker.cc b/libgui/src/m-editor/marker.cc
new file mode 100644
--- /dev/null
+++ b/libgui/src/m-editor/marker.cc
@@ -0,0 +1,192 @@
+/*
+
+Copyright (C) 2013 Daniel J. Sebald
+
+This file is part of Octave.
+
+Octave is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 3 of the License, or (at your
+option) any later version.
+
+Octave is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with Octave; see the file COPYING.  If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_QSCINTILLA
+
+#include <stdio.h>
+
+#include "marker.h"
+
+
+marker::marker (QsciScintilla *area, int original_linenr, editor_markers type,
+                int editor_linenr) : QObject ()
+{
+  construct (area, original_linenr, type, editor_linenr);
+}
+
+
+marker::marker (QsciScintilla *area, int original_linenr,
+                editor_markers type) : QObject ()
+{
+  construct (area, original_linenr, type, original_linenr - 1);
+}
+
+
+marker::~marker (void)
+{
+}
+
+
+void
+marker::construct (QsciScintilla *area, int original_linenr,
+                   editor_markers type, int editor_linenr)
+{
+  _edit_area = area;
+  _original_linenr = original_linenr;
+  _marker_type = type;
+  _mhandle = _edit_area->markerAdd (editor_linenr, _marker_type);
+
+/* NOT OF MUCH USE, EVENTUALLY REMOVE
+  connect (_edit_area, SIGNAL (linesChanged (void)),
+           this, SLOT (handle_lines_changed (void)));
+*/
+}
+
+
+void
+marker::handle_remove_via_original_linenr (int linenr)
+{
+  if (_original_linenr == linenr)
+    {
+      _edit_area->markerDeleteHandle(_mhandle);
+      delete this;
+    }
+}
+
+
+void
+marker::handle_request_remove_via_editor_linenr (int linenr)
+{
+  // Get line number from the edit area and if it matches
+  // the requested line number, remove.
+  if (_edit_area->markerLine (_mhandle) == linenr)
+    {
+      // Rather than delete editor marker directly, issue command
+      // to Octave core.  Octave core should signal back to remove
+      // this breakpoint via debugger line number.
+      emit request_remove (_original_linenr);
+    }
+}
+
+
+void
+marker::handle_remove (void)
+{
+  _edit_area->markerDeleteHandle (_mhandle);
+  delete this;
+}
+
+
+void
+marker::handle_find_translation (int linenr, int& translation_linenr)
+{
+  if (_original_linenr == linenr)
+    translation_linenr = _edit_area->markerLine (_mhandle);
+}
+
+
+void
+marker::handle_find_just_before (int linenr, int& original_linenr, int& editor_linenr)
+{
+  if (_original_linenr < linenr && _original_linenr >= original_linenr)
+    {
+      original_linenr = _original_linenr;
+      editor_linenr = _edit_area->markerLine (_mhandle);
+    }
+}
+
+
+void
+marker::handle_find_just_after (int linenr, int& original_linenr, int& editor_linenr)
+{
+  if (_original_linenr > linenr && _original_linenr <= original_linenr)
+    {
+      original_linenr = _original_linenr;
+      editor_linenr = _edit_area->markerLine (_mhandle);
+    }
+}
+
+
+void
+marker::handle_report_editor_linenr (QIntList& list)
+{
+  list << _edit_area->markerLine (_mhandle);
+}
+
+
+/* NOT OF MUCH USE, EVENTUALLY REMOVE
+void
+marker::handle_lines_changed (void)
+{
+fprintf(stderr,"marker line number: %d\n", _edit_area->markerLine (_mhandle));
+}
+*/
+
+
+void
+marker::handle_marker_line_deleted (int mhandle)
+{
+  // FUTURE SUPPORT: There really should be a signal in QsciScintilla
+  // called markerLineDeleted (int mhandle) because there is no way
+  // of knowing this.  QsciScintilla will place the marker at a
+  // different line rather than remove it from the margin.  I (DJS) will
+  // lobby for such a signal.
+  if (_mhandle == mhandle)
+    {
+      if (_marker_type == breakpoint || _marker_type == debugger_position)
+        {
+          int editor_linenr = _edit_area->markerLine (_mhandle);
+          _edit_area->markerDeleteHandle(_mhandle);
+          _marker_type = _marker_type == breakpoint ? unsure_breakpoint
+                                                    : unsure_debugger_position;
+          _mhandle = _edit_area->markerAdd (editor_linenr, _marker_type);
+        }
+    }
+}
+
+
+void
+marker::handle_marker_line_undeleted (int mhandle)
+{
+  // FUTURE SUPPORT: There really should be a signal in QsciScintilla
+  // called markerLineUndeleted (int mhandle) because there is no way
+  // of knowing this.  QsciScintilla will place the marker at a
+  // different line rather than remove it from the margin.  I (DJS) will
+  // lobby for such a signal.
+  if (_mhandle == mhandle)
+    {
+      if (_marker_type == unsure_breakpoint || _marker_type == unsure_debugger_position)
+        {
+          int editor_linenr = _edit_area->markerLine (_mhandle);
+          _edit_area->markerDeleteHandle(_mhandle);
+          _marker_type = _marker_type == unsure_breakpoint ? breakpoint
+                                                           : debugger_position;
+          _mhandle = _edit_area->markerAdd (editor_linenr, _marker_type);
+        }
+    }
+}
+
+#endif
diff --git a/libgui/src/m-editor/marker.h b/libgui/src/m-editor/marker.h
new file mode 100644
--- /dev/null
+++ b/libgui/src/m-editor/marker.h
@@ -0,0 +1,86 @@
+/*
+
+Copyright (C) 2013 Daniel J. Sebald
+
+This file is part of Octave.
+
+Octave is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 3 of the License, or (at your
+option) any later version.
+
+Octave is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with Octave; see the file COPYING.  If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#ifndef MARKER_H
+#define MARKER_H
+
+#include <Qsci/qsciscintilla.h>
+#include <QObject>
+
+// Defined for purposes of sending QList<int> as part of signal.
+#include <QList>
+typedef QList<int> QIntList;
+
+// The breakpoint class keeps track of the debug line number that Octave core
+// uses and the handle of the marker inside the editor file.  If the editor
+// contents is modified, the debug line number and editor line number can be
+// out of alignment.  The marker handle can be used to retrieve the editor
+// line.
+
+class marker;
+class marker : public QObject
+{
+  Q_OBJECT
+
+public:
+
+  enum editor_markers
+    {
+      bookmark,
+      breakpoint,
+      unsure_breakpoint,
+      debugger_position,
+      unsure_debugger_position
+    };
+
+  marker (QsciScintilla *edit_area, int original_linenr,
+          editor_markers marker_type);
+  marker (QsciScintilla *edit_area, int original_linenr,
+          editor_markers marker_type, int editor_linenr);
+  ~marker (void);
+
+public slots:
+  void handle_remove_via_original_linenr (int original_linenr);
+  void handle_request_remove_via_editor_linenr (int editor_linenr);
+  void handle_remove (void);
+  void handle_find_translation (int original_linenr, int& editor_linenr);
+  void handle_find_just_before (int linenr, int& original_linenr, int& editor_linenr);
+  void handle_find_just_after (int linenr, int& original_linenr, int& editor_linenr);
+/*  void handle_lines_changed (void);*/
+  void handle_marker_line_deleted (int mhandle);
+  void handle_marker_line_undeleted (int mhandle);
+  void handle_report_editor_linenr (QIntList& int_list);
+
+signals:
+  void request_remove (int original_linenr);
+
+private:
+  void construct (QsciScintilla *edit_area, int original_linenr,
+                  editor_markers marker_type, int editor_linenr);
+
+  QsciScintilla *       _edit_area;
+  int                   _original_linenr;
+  editor_markers        _marker_type;
+  int                   _mhandle;
+};
+
+#endif // MARKER_H
diff --git a/libgui/src/module.mk b/libgui/src/module.mk
--- a/libgui/src/module.mk
+++ b/libgui/src/module.mk
@@ -6,6 +6,7 @@
 
 octave_gui_ICONS = \
   src/icons/arrow_right.png \
+  src/icons/arrow_right_gray.png \
   src/icons/artsbuilderexecute.png \
   src/icons/bookmark.png \
   src/icons/bp_next.png \
@@ -51,6 +52,7 @@
   src/icons/ok.png \
   src/icons/question.png \
   src/icons/redled.png \
+  src/icons/redled_gray.png \
   src/icons/redo.png \
   src/icons/reload.png \
   src/icons/search.png \
@@ -70,7 +72,8 @@
   src/m-editor/moc-file-editor-tab.cc \
   src/m-editor/moc-file-editor.cc \
   src/m-editor/moc-find-dialog.cc \
-  src/m-editor/moc-lexer-octave-gui.cc
+  src/m-editor/moc-lexer-octave-gui.cc \
+  src/m-editor/moc-marker.cc
 endif
 
 octave_gui_MOC += \
@@ -112,6 +115,7 @@
   src/m-editor/file-editor.h \
   src/m-editor/find-dialog.h \
   src/m-editor/lexer-octave-gui.h \
+  src/m-editor/marker.h \
   src/main-window.h \
   src/octave-gui.h \
   src/octave-main-thread.h \
@@ -137,6 +141,7 @@
   src/m-editor/file-editor.cc \
   src/m-editor/find-dialog.cc \
   src/m-editor/lexer-octave-gui.cc \
+  src/m-editor/marker.cc \
   src/main-window.cc \
   src/octave-gui.cc \
   src/octave-main-thread.cc \
diff --git a/libgui/src/resource.qrc b/libgui/src/resource.qrc
--- a/libgui/src/resource.qrc
+++ b/libgui/src/resource.qrc
@@ -20,7 +20,9 @@
         <file>icons/configure.png</file>
         <file>icons/filesaveas.png</file>
         <file>icons/redled.png</file>
+        <file>icons/redled_gray.png</file>
         <file>icons/arrow_right.png</file>
+        <file>icons/arrow_right_gray.png</file>
         <file>icons/bookmark.png</file>
         <file>icons/question.png</file>
         <file>icons/star.png</file>
diff --git a/libgui/src/settings-dialog.cc b/libgui/src/settings-dialog.cc
--- a/libgui/src/settings-dialog.cc
+++ b/libgui/src/settings-dialog.cc
@@ -80,6 +80,31 @@
   ui->general_icon_graphic-> setChecked (widget_icon_set == "GRAPHIC");
   ui->general_icon_letter-> setChecked (widget_icon_set == "LETTER");
 
+  // how breakpoints should behave when file is saved
+  QString breakpoint_filesave_behavior =
+      settings->value ("debugger/breakpoint_filesave_behavior", "ASK").toString ();
+  ui->debugger_filesave_ask->setChecked (true);  // the default (if invalid set)
+  ui->debugger_filesave_ask->setChecked (breakpoint_filesave_behavior == "ASK");
+  ui->debugger_filesave_restore->setChecked (breakpoint_filesave_behavior == "RESTORE");
+  ui->debugger_filesave_discard->setChecked (breakpoint_filesave_behavior == "DISCARD");
+
+  // what uncertain breakpoints and pointer should look like
+  QString uncertain_icon_set =
+      settings->value ("debugger/uncertain_icon_set", "SELECTED").toString ();
+  QPixmap tmpPixmap = QPixmap (":/actions/icons/arrow_right.png");
+  QIcon tmpIcon = QIcon (tmpPixmap);
+  tmpPixmap = tmpIcon.pixmap (tmpPixmap.size (), QIcon::Selected);
+  ui->debugger_icon_selected->setIcon (QIcon (tmpPixmap));
+  ui->debugger_icon_selected->setChecked (true);  // the default (if invalid set)
+  ui->debugger_icon_selected->setChecked (uncertain_icon_set == "SELECTED");
+  tmpPixmap = QPixmap (":/actions/icons/arrow_right.png");
+  tmpIcon = QIcon (tmpPixmap);
+  tmpPixmap = tmpIcon.pixmap (tmpPixmap.size (), QIcon::Disabled);
+  ui->debugger_icon_disabled->setIcon (QIcon (tmpPixmap));
+  ui->debugger_icon_disabled->setChecked (uncertain_icon_set == "DISABLED");
+  ui->debugger_icon_custom->setIcon (QIcon (QPixmap (":/actions/icons/arrow_right_gray.png")));
+  ui->debugger_icon_custom->setChecked (uncertain_icon_set == "CUSTOM");
+
   ui->useCustomFileEditor->setChecked (settings->value ("useCustomFileEditor",false).toBool ());
   ui->customFileEditor->setText (settings->value ("customFileEditor").toString ());
   ui->editor_showLineNumbers->setChecked (settings->value ("editor/showLineNumbers",true).toBool () );
@@ -339,13 +364,29 @@
   QSettings *settings = resource_manager::get_settings ();
   // FIXME -- what should happen if settings is 0?
 
+  // what uncertain breakpoints and pointer should look like
+  QString uncertain_icon_set = "SELECTED";
+  if (ui->debugger_icon_disabled->isChecked ())
+    uncertain_icon_set = "DISABLED";
+  else if (ui->debugger_icon_custom->isChecked ())
+    uncertain_icon_set = "CUSTOM";
+  settings->setValue ("debugger/uncertain_icon_set", uncertain_icon_set);
+
+  // how breakpoints should be treated when file is saved
+  QString breakpoint_filesave_behavior = "ASK";
+  if (ui->debugger_filesave_restore->isChecked ())
+    breakpoint_filesave_behavior = "RESTORE";
+  else if (ui->debugger_filesave_discard->isChecked ())
+    breakpoint_filesave_behavior = "DISCARD";
+  settings->setValue ("debugger/breakpoint_filesave_behavior", breakpoint_filesave_behavior);
+
   // the icon set
   QString widget_icon_set = "NONE";
   if (ui->general_icon_letter->isChecked ())
     widget_icon_set = "LETTER";
   else if (ui->general_icon_graphic->isChecked ())
     widget_icon_set = "GRAPHIC";
-  settings->setValue ("DockWidgets/widget_icon_set",widget_icon_set);
+  settings->setValue ("DockWidgets/widget_icon_set", widget_icon_set);
 
   // language
   QString language = ui->comboBox_language->currentText ();
diff --git a/libgui/src/settings-dialog.ui b/libgui/src/settings-dialog.ui
--- a/libgui/src/settings-dialog.ui
+++ b/libgui/src/settings-dialog.ui
@@ -479,6 +479,103 @@
        </layout>
       </widget>
      </widget>
+     <widget class="QWidget" name="tab_7">
+      <attribute name="title">
+       <string>Debugger</string>
+      </attribute>
+      <widget class="QWidget" name="verticalLayoutWidget_2">
+       <property name="geometry">
+        <rect>
+         <x>30</x>
+         <y>20</y>
+         <width>481</width>
+         <height>301</height>
+        </rect>
+       </property>
+       <layout class="QVBoxLayout" name="verticalLayout_8">
+        <item>
+         <widget class="QGroupBox" name="groupBox_3">
+          <property name="title">
+           <string>Uncertain Position/Breakpoint Icon Set</string>
+          </property>
+          <widget class="QWidget" name="verticalLayoutWidget_4">
+           <property name="geometry">
+            <rect>
+             <x>10</x>
+             <y>30</y>
+             <width>325</width>
+             <height>80</height>
+            </rect>
+           </property>
+           <layout class="QVBoxLayout" name="verticalLayout_10">
+            <item>
+             <widget class="QRadioButton" name="debugger_icon_selected">
+              <property name="text">
+               <string>Selected</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QRadioButton" name="debugger_icon_disabled">
+              <property name="text">
+               <string>Disabled</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QRadioButton" name="debugger_icon_custom">
+              <property name="text">
+               <string>Custom</string>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </widget>
+         </widget>
+        </item>
+        <item>
+         <widget class="QGroupBox" name="groupBox_2">
+          <property name="title">
+           <string>Breakpoint Retention Upon Saving File</string>
+          </property>
+          <widget class="QWidget" name="verticalLayoutWidget_3">
+           <property name="geometry">
+            <rect>
+             <x>10</x>
+             <y>30</y>
+             <width>325</width>
+             <height>80</height>
+            </rect>
+           </property>
+           <layout class="QVBoxLayout" name="verticalLayout_9">
+            <item>
+             <widget class="QRadioButton" name="debugger_filesave_ask">
+              <property name="text">
+               <string>Ask whether breakpoints should be restored</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QRadioButton" name="debugger_filesave_restore">
+              <property name="text">
+               <string>Automatically restore breakpoints</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QRadioButton" name="debugger_filesave_discard">
+              <property name="text">
+               <string>Discard breakpoints</string>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </widget>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </widget>
      <widget class="QWidget" name="tab_2">
       <attribute name="title">
        <string>File Browser</string>
