[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[gnue] r8653 - in trunk/gnue-forms/src: . GFObjects
From: |
reinhard |
Subject: |
[gnue] r8653 - in trunk/gnue-forms/src: . GFObjects |
Date: |
Tue, 5 Sep 2006 15:27:09 -0500 (CDT) |
Author: reinhard
Date: 2006-09-05 15:27:09 -0500 (Tue, 05 Sep 2006)
New Revision: 8653
Modified:
trunk/gnue-forms/src/GFForm.py
trunk/gnue-forms/src/GFInstance.py
trunk/gnue-forms/src/GFObjects/GFBlock.py
Log:
Restructured record navigation methods.
Modified: trunk/gnue-forms/src/GFForm.py
===================================================================
--- trunk/gnue-forms/src/GFForm.py 2006-09-05 15:18:40 UTC (rev 8652)
+++ trunk/gnue-forms/src/GFForm.py 2006-09-05 20:27:09 UTC (rev 8653)
@@ -921,7 +921,17 @@
field.processTrigger(post, ignoreAbort = False)
+ # -------------------------------------------------------------------------
+ # Info about focus position
+ # -------------------------------------------------------------------------
+ def get_focus_block(self):
+ """
+ Return the block that currently has the focus.
+ """
+ return self._currentBlock
+
+
# =========================================================================
# Events
#
@@ -1428,14 +1438,14 @@
# If we should navigate to the next record, do it...
if currentBlock is not None \
and reverse and not currentBlock.isFirstRecord():
- currentBlock.prevRecord()
+ currentBlock.prev_record()
self.changeFocus(nextEntry)
elif currentBlock is not None and not reverse and \
currentBlock.autoNextRecord and \
not currentBlock.isEmpty() and \
not (not currentBlock.autoCreate and \
currentBlock.isLastRecord()):
- currentBlock.nextRecord()
+ currentBlock.next_record()
# If new record is empty, then the
# nextEntry logic has been taken care of...
@@ -1636,81 +1646,95 @@
# -------------------------------------------------------------------------
- # Step to previous record in block
+ # Record navigation
# -------------------------------------------------------------------------
- def prevRecord(self):
+ def first_record(self):
"""
+ Jumps to the first record in the current block.
+ """
+ if self._currentBlock is not None:
+ self._currentBlock.first_record()
+
+ # -------------------------------------------------------------------------
+
+ def prev_record(self):
+ """
Steps to the previous record in the current block.
- @return: None
"""
- if self._currentBlock.mode == 'query':
- self.status_message(u_('You cannot do that in query mode.'))
- return
- self._currentBlock.prevRecord()
+ if self._currentBlock is not None:
+ self._currentBlock.prev_record()
-
# -------------------------------------------------------------------------
- # Jump to first record in block.
- # -------------------------------------------------------------------------
- def firstRecord(self):
+ def next_record(self):
"""
- Jumps to the first record in the current block.
- @return: None
+ Steps to the next record in the current block.
"""
- if self._currentBlock.mode == 'query':
- self.status_message(u_('You cannot do that in query mode.'))
- return
- self._currentBlock.firstRecord()
+ if self._currentBlock is not None:
+ self._currentBlock.next_record()
-
# -------------------------------------------------------------------------
- # Jump to last record in block.
- # -------------------------------------------------------------------------
- def lastRecord(self):
+ def last_record(self):
"""
Jumps to the last record in the current block.
- @return: None
"""
- if self._currentBlock.mode == 'query':
- self.status_message(u_('You cannot do that in query mode.'))
- return
- self._currentBlock.lastRecord()
+ if self._currentBlock is not None:
+ self._currentBlock.last_record()
-
# -------------------------------------------------------------------------
- # Step to next record in block.
- # -------------------------------------------------------------------------
- def nextRecord(self):
+ def ask_record(self):
"""
- Steps to the next record in the current block.
- @return: None
+ Ask the user for a record number to jump to in the current block.
"""
- if self._currentBlock.mode == 'query':
- self.status_message(u_('You cannot do that in query mode.'))
- return
- self._currentBlock.nextRecord()
+ if self._currentBlock is None:
+ return
+ fields = [(u_("Recordnumber"), 'record', 'string',
+ str(self._currentBlock._currentRecord + 1), None, [])]
+
+ while True:
+ result = self._instance._uiinstance.getInput(u_("Jump to record"),
+ fields)
+ if result is None:
+ return
+ try:
+ count = int(result['record'])
+ # Positive start to count by zero
+ if count > 0: count -= 1
+ break
+ except ValueError:
+ fields = [
+ (u_("Invalid numeric value entered."), None, 'warning',
+ None, None, []),
+ (u_("Recordnumber"), 'record', 'string',
+ result['record'], None, [])]
+ self.goto_record(count)
+
# -------------------------------------------------------------------------
- # Jump to a given record in block.
- # -------------------------------------------------------------------------
- def jumpRecord(self, count):
+ def goto_record(self, count):
"""
Jumps to a given record in the current block.
@param count: ordinal of the record
- @return: None
"""
- if self._currentBlock.mode == 'query':
- self.status_message(u_('You cannot do that in query mode.'))
- return
- self._currentBlock.jumpRecord(count)
+ if self._currentBlock is not None:
+ self._currentBlock.goto_record(count)
+ # -------------------------------------------------------------------------
+ def jump_records(self, count):
+ """
+ Jumps to a given record in the current block.
+ @param count: number of records to move
+ """
+ if self._currentBlock is not None:
+ self._currentBlock.jump_records(count)
+
+
# -------------------------------------------------------------------------
# Toggles insert mode
# -------------------------------------------------------------------------
Modified: trunk/gnue-forms/src/GFInstance.py
===================================================================
--- trunk/gnue-forms/src/GFInstance.py 2006-09-05 15:18:40 UTC (rev 8652)
+++ trunk/gnue-forms/src/GFInstance.py 2006-09-05 20:27:09 UTC (rev 8653)
@@ -618,191 +618,102 @@
# ---------------------------------------------------------------------------
- # Jump to the previous record
+ # Record navigation
# ---------------------------------------------------------------------------
- def prevRecord (self, event):
+ def firstRecord(self, event):
"""
- Called whenever an event source has requested that the form moves to the
- previous record in memory
+ Called whenever an event source has requested that the form advance to the
+ first record in memory
"""
+ event._form.first_record()
- if not event._form.endEditing ():
- return
-
- message = event._form.prevRecord ()
- if message:
- self.displayMessageBox (message)
- return
-
- self._entryUpdated (event._form)
-
-
# ---------------------------------------------------------------------------
- # Jump to the next record
- # ---------------------------------------------------------------------------
- def nextRecord (self, event):
+ def prevRecord(self, event):
"""
- Called whenever an event source has requested that the form advance to the
- next record in memory
+ Called whenever an event source has requested that the form moves to the
+ previous record in memory
"""
+ event._form.prev_record()
- if not event._form.endEditing ():
- return
-
- message = event._form.nextRecord ()
- if message:
- self.displayMessageBox (message)
- return
-
- self._entryUpdated (event._form)
-
-
# ---------------------------------------------------------------------------
- # Jump to the first record
- # ---------------------------------------------------------------------------
- def firstRecord (self, event):
+ def nextRecord(self, event):
"""
Called whenever an event source has requested that the form advance to the
- first record in memory
+ next record in memory
"""
- if not event._form.endEditing ():
- return
+ event._form.next_record()
- message = event._form.firstRecord ()
- if message:
- self.displayMessageBox (message)
- return
-
- self._entryUpdated (event._form)
-
-
# ---------------------------------------------------------------------------
- # Jump to the last record
- # ---------------------------------------------------------------------------
- def lastRecord (self, event):
+ def lastRecord(self, event):
"""
Called enever an event source has requested that the form advance to the
last record in memory
"""
+ event._form.last_record()
- if not event._form.endEditing ():
- return
+ # ---------------------------------------------------------------------------
- message = event._form.lastRecord ()
- if message:
- self.displayMessageBox (message)
- return
+ def requestJumpTo(self,event):
+ """
+ Displays a dialog box prompting for record to jump to then jumps to the
+ requested record
+ """
+ event._form.ask_record()
- self._entryUpdated (event._form)
-
-
- #
# ---------------------------------------------------------------------------
- # Jump to a specific record
- # ---------------------------------------------------------------------------
- def jumpToRecord (self, event):
+ def jumpToRecord(self, event):
"""
Called whenever an event source has requested that the form move to a
specific record
"""
+ count = abs(int(event.data)) - 1
+ event._form.goto_record(count)
- if event._form._currentBlock.mode == 'query':
- event._form.status_message (u_('You cannot do that in query mode.'))
- return
-
- if not event._form.endEditing ():
- return
-
- try:
- count = abs (int (event.data)) - 1
-
- except ValueError:
- message = _("Invalid numeric value entered.")
-
- else:
- message = event._form.jumpRecord (count)
-
- if message:
- self.displayMessageBox (message, 'Error')
- return
-
- self._entryUpdated (event._form)
-
-
# ---------------------------------------------------------------------------
- # Jump records forward or backward
- # ---------------------------------------------------------------------------
- def jumpRecords (self, event):
+ def jumpRecords(self, event):
"""
Jump a specified number of records forward or backward.
"""
+ event._form.jump_records(event.data)
- if not event._form.endEditing ():
- return
-
- # Manipulate event and redirect to jumpToRecord
- rows = event.data
- curRec = event._form._currentBlock._currentRecord + 1
- recCount = event._form._currentBlock._recordCount
-
- newValue = max (1, curRec + rows)
- event.data = min (recCount, newValue)
-
- self.jumpToRecord (event)
- self._entryUpdated (event._form)
-
-
# ---------------------------------------------------------------------------
- # Jump a number of records backward
- # ---------------------------------------------------------------------------
- def jumpRowsUp (self, event):
+ def jumpRowsUp(self, event):
- if not event._form.endEditing ():
- return
-
- # Manipulate event and redirect to jumpToRecord
if event._form._currentEntry._rows > 1:
- event.data = -event._form._currentEntry._rows + 1
+ count = -event._form._currentEntry._rows + 1
elif event._form._currentBlock._rows >1:
- event.data = -event._form._currentBlock._rows + 1
+ count = -event._form._currentBlock._rows + 1
else:
- event.data = 0
+ count = 0
- if event.data:
- self.jumpRecords (event)
+ if count:
+ event._form.jump_records(count)
-
# ---------------------------------------------------------------------------
- # Jump a number of records forward
- # ---------------------------------------------------------------------------
def jumpRowsDown (self, event):
- if not event._form.endEditing ():
- return
-
- # Manipulate event and redirect to jumpToRecord
if event._form._currentEntry._rows > 1:
- event.data = event._form._currentEntry._rows - 1
+ count = event._form._currentEntry._rows - 1
elif event._form._currentBlock._rows >1:
- event.data = event._form._currentBlock._rows - 1
+ count = event._form._currentBlock._rows - 1
else:
- event.data = 0
+ count = 0
- if event.data:
- self.jumpRecords (event)
+ if count:
+ event._form.jump_records(count)
# ---------------------------------------------------------------------------
@@ -882,43 +793,7 @@
return self._uiinstance.showMessage (message, kind, title, cancel)
- #
- # requestJumpTo
- #
- #
- # ---------------------------------------------------------------------------
- # Display a dialog box prompting for a record number to jump to
- # ---------------------------------------------------------------------------
- def requestJumpTo(self,event):
- """
- Displays a dialog box prompting for record to jump to then jumps to the
- requested record
- """
-
- fields = [(u_("Recordnumber"), 'record', 'string', None, None, [])]
- result = self._uiinstance.getInput (u_("Jump to record"), fields)
-
- if result is not None:
- if not event._form.endEditing ():
- return
-
- try:
- count = abs (int (float (result ['record']))) - 1
-
- except ValueError:
- message = _("Invalid numeric value entered.")
-
- else:
- message = event._form.jumpRecord (count)
-
- if message:
- self.displayMessageBox (message, 'Error')
- return
-
- self._entryUpdated (event._form)
-
-
# ---------------------------------------------------------------------------
# Toggle insert mode
# ---------------------------------------------------------------------------
Modified: trunk/gnue-forms/src/GFObjects/GFBlock.py
===================================================================
--- trunk/gnue-forms/src/GFObjects/GFBlock.py 2006-09-05 15:18:40 UTC (rev
8652)
+++ trunk/gnue-forms/src/GFObjects/GFBlock.py 2006-09-05 20:27:09 UTC (rev
8653)
@@ -134,12 +134,6 @@
'function': self.duplicateRecord,
'description': 'Duplicates the current (non-empty) record into a ' \
'new record.'},
- 'gotoRecord': {
- 'function' : self.jumpRecord,
- 'description': ''},
- 'firstRecord': {
- 'function' : self.firstRecord,
- 'description': 'Navigates the block to the first record it contains.'},
'isEmpty': {
'function' : self.isEmpty,
'description': 'Returns True if block is empty.'},
@@ -150,18 +144,25 @@
'isPending': {
'function' : self.isPending,
'description': 'Returns True if block contains modified records.'},
- 'lastRecord': {
- 'function' : self.lastRecord,
- 'description': 'Navigates the block to the last record it contains.'},
- 'nextRecord': {
- 'function' : self.nextRecord,
- 'description': 'Navigates the block to the next record in sequence.'},
+
+ 'firstRecord': {
+ 'function' : self.first_record,
+ 'description': 'Navigates the block to the first record it contains.'},
'prevRecord': {
- 'function' : self.prevRecord,
+ 'function' : self.prev_record,
'description': 'Navigates the block to the previous record in ' \
'sequence.'},
+ 'nextRecord': {
+ 'function' : self.next_record,
+ 'description': 'Navigates the block to the next record in sequence.'},
+ 'lastRecord': {
+ 'function' : self.last_record,
+ 'description': 'Navigates the block to the last record it contains.'},
+ 'gotoRecord': {
+ 'function' : self.goto_record,
+ 'description': 'Navigates the block to the specified record number.'},
'jumpRecords': {
- 'function' : self.jumpRecords,
+ 'function' : self.jump_records,
'description': 'Navigates the specified number of records.'},
'rollback': {
'function' : self.processRollback,
@@ -661,148 +662,180 @@
# Record Navigation
# ---------------------------------------------------------------------------
- def firstRecord (self):
- """
- Move to the first record of the block. Pre- and Post-Focusout triggers are
- fired before moving as well as Pre- and Post-Focusin triggers after moving
- the record pointer.
- """
+ def first_record(self):
+ """
+ Move to the first record of the block. Pre- and Post-Focusout triggers
+ are fired before moving as well as Pre- and Post-Focusin triggers after
+ moving the record pointer.
+ """
- if not self._resultSet.isFirstRecord ():
- # Do FocusOut triggers
- self.processTrigger ('PRE-FOCUSOUT')
- self.processTrigger ('POST-FOCUSOUT')
+ if self.mode == 'query':
+ return
- self._resultSet.firstRecord ()
+ if self._resultSet.isFirstRecord():
+ return
- # Focus in
- self.processTrigger ('PRE-FOCUSIN')
- self.processTrigger ('POST-FOCUSIN')
+ if not self._form.endEditing():
+ return
+ self.__focus_out()
+
+ self._resultSet.firstRecord()
+
+ self.__focus_in()
+
+ self._form.dispatchEvent('gotoENTRY',
+ object=self._form._currentEntry, _form=self._form)
+
+ self._form.beginEditing()
+
# ---------------------------------------------------------------------------
- def prevRecord (self):
- """
- Move to the previous record of the block. Pre- and Post-Focusout triggers
- are fired before moving as well as Pre- and Post-Focusin triggers after
- moving the record pointer.
- """
+ def prev_record(self):
+ """
+ Move to the previous record of the block. Pre- and Post-Focusout
+ triggers are fired before moving as well as Pre- and Post-Focusin
+ triggers after moving the record pointer.
+ """
- if not self._resultSet.isFirstRecord ():
- # Do FocusOut triggers
- self.processTrigger ('PRE-FOCUSOUT')
- self.processTrigger ('POST-FOCUSOUT')
+ if self.mode == 'query':
+ return
- self._resultSet.prevRecord ()
+ if self._resultSet.isFirstRecord():
+ return
- # Focus in
- self.processTrigger ('PRE-FOCUSIN')
- self.processTrigger ('POST-FOCUSIN')
+ if not self._form.endEditing():
+ return
+ self.__focus_out()
+
+ self._resultSet.prevRecord()
+
+ self.__focus_in()
+
+ self._form.dispatchEvent('gotoENTRY',
+ object=self._form._currentEntry, _form=self._form)
+
+ self._form.beginEditing()
+
# ---------------------------------------------------------------------------
- def nextRecord (self):
- """
- Move to the next record. If autoCommit is set a commit is performed prior
- to move the record pointer. If the record is already the last one, a new
- record will be created if the following conditions are met: autoCreate is
- True, there are already records available in the block and the block is
- editable.
- """
+ def next_record(self):
+ """
+ Move to the next record. If autoCommit is set a commit is performed
+ prior to move the record pointer. If the record is already the last
+ one, a new record will be created if the following conditions are met:
+ autoCreate is True, there are already records available in the block
+ and the block is editable.
+ """
- if not self._resultSet.isLastRecord ():
- # Do FocusOut triggers
- self.processTrigger ('PRE-FOCUSOUT')
- if self.autoCommit:
- self._form.commit ()
- self.processTrigger ('POST-FOCUSOUT')
+ if self.mode == 'query':
+ return
- self._resultSet.nextRecord ()
- self._recordCount = self._resultSet.getRecordCount ()
+ if self._resultSet.isLastRecord():
+ if self.autoCreate and not self.isEmpty() and \
+ not self.editable in ('update', 'N'):
+ self.new_record()
+ return
- # Focus in
- self.processTrigger ('PRE-FOCUSIN')
- self.processTrigger ('POST-FOCUSIN')
+ if not self._form.endEditing():
+ return
- elif self.autoCreate and not self.isEmpty () and \
- not self.editable in ('update', 'N'):
- self.new_record()
+ self.__focus_out()
- # Go to first field
- # TODO: check if this is really what we want to have
- self._form.findAndChangeFocus (self._entryList [0])
+ self._resultSet.nextRecord()
+ self.__focus_in()
+
+ self._form.dispatchEvent('gotoENTRY',
+ object=self._form._currentEntry, _form=self._form)
+
+ self._form.beginEditing()
+
# ---------------------------------------------------------------------------
- def lastRecord (self):
- """
- Move to the last record of the block. Pre- and Post-Focusout triggers are
- fired before moving as well as Pre- and Post-Focusin triggers after moving
- the record pointer.
- """
+ def last_record(self):
+ """
+ Move to the last record of the block. Pre- and Post-Focusout triggers
+ are fired before moving as well as Pre- and Post-Focusin triggers after
+ moving the record pointer.
+ """
- if not self._resultSet.isLastRecord ():
- # Do FocusOut triggers
- self.processTrigger ('PRE-FOCUSOUT')
- self.processTrigger ('POST-FOCUSOUT')
+ if self.mode == 'query':
+ return
- self._resultSet.lastRecord ()
+ if self._resultSet.isLastRecord():
+ return
- # Focus in
- self.processTrigger ('PRE-FOCUSIN')
- self.processTrigger ('POST-FOCUSIN')
+ if not self._form.endEditing():
+ return
+ self.__focus_out()
+
+ self._resultSet.lastRecord()
+
+ self.__focus_in()
+
+ self._form.dispatchEvent('gotoENTRY',
+ object=self._form._currentEntry, _form=self._form)
+
+ self._form.beginEditing()
+
# ---------------------------------------------------------------------------
- def jumpRecord (self, recordNumber):
- """
- Jump to a specific record. If the block is the block being currently edited
- in the form, the focus-out and focus-in block-level triggers will be fired.
+ def goto_record(self, record_number):
+ """
+ Jump to a specific record. If the block is the block being currently
+ edited in the form, the focus-out and focus-in block-level triggers
+ will be fired.
- @param recordNumber: record number to jump to. If this is a negative value,
- move relative to the last record
- """
+ @param record_number: record number to jump to. If this is a negative
+ value, move relative to the last record
+ """
- # If recordNumber is negative, move relative to last record
- if recordNumber < 0:
- recordNumber += self._resultSet.getRecordCount ()
- if recordNumber < 0:
- raise "Invalid record number"
+ if self.mode == 'query':
+ return
- if recordNumber != self._resultSet.getRecordNumber ():
- if getattr(self._form._currentEntry, '_block', None) == self:
- # Focus out
- self.processTrigger ('PRE-FOCUSOUT')
- self.processTrigger ('POST-FOCUSOUT')
+ if not self._form.endEditing():
+ return
- if not self._resultSet.setRecord (recordNumber):
- self._resultSet.lastRecord ()
+ # If record_number is negative, move relative to last record
+ if record_number < 0:
+ record_number += self._resultSet.getRecordCount()
+ if record_number < 0:
+ record_number = 0
- if getattr(self._form._currentEntry, '_block', None) == self:
- # Focus in
- self.processTrigger ('PRE-FOCUSIN')
- self.processTrigger ('POST-FOCUSIN')
+ if record_number == self._resultSet.getRecordNumber():
+ return
- # Move to correct record in grid
- self._form.findAndChangeFocus (self._form._currentEntry)
- self._form.update_record_counter()
+ self.__focus_out()
+ if not self._resultSet.setRecord(record_number):
+ self._resultSet.lastRecord()
+
+ self.__focus_in()
+
+ self._form.dispatchEvent('gotoENTRY',
+ object=self._form._currentEntry, _form=self._form)
+
+ self._form.beginEditing()
+
# ---------------------------------------------------------------------------
- def jumpRecords (self, adjustment):
- """
- Move the record-pointer by a given adjustment relative to the current
- record.
+ def jump_records(self, count):
+ """
+ Move the record-pointer by a given adjustment relative to the current
+ record.
- @param adjustment: the number of records to move from the current record.
- """
+ @param count: the number of records to move from the current record.
+ """
- targetRecord = max (self._resultSet.getRecordNumber() + adjustment, 0)
+ record_number = self._resultSet.getRecordNumber() + count
- if targetRecord > self._resultSet.getRecordCount ():
- targetRecord = self._resultSet.getRecordCount()
+ record_number = max(record_number, 0)
+ record_number = min(record_number, self._resultSet.getRecordCount())
- self.jumpRecord (targetRecord)
+ self.goto_record(record_number)
# ---------------------------------------------------------------------------
@@ -815,33 +848,19 @@
currently pending it will be commited first.
"""
- if not self._form.endEditing ():
+ if not self._form.endEditing():
return
- # FIXME: GFForm.readonly and GFBlock.editable should be tested before
- # enabling the menu item and the toolbar button. Once that is done, the
- # tests here could be removed so trigger code can still insert new
- # records while the user can't.
+ self.__focus_out()
- # Focus out
- self.processTrigger ('PRE-FOCUSOUT')
-
- if self.autoCommit and self._resultSet.current:
- self._form.commit ()
-
- self.processTrigger ('POST-FOCUSOUT')
-
self._resultSet.insertRecord(self._lastValues)
- self._recordCount = self._resultSet.getRecordCount ()
+ self._recordCount = self._resultSet.getRecordCount()
- # Focus in
- self.processTrigger ('PRE-FOCUSIN')
- self.processTrigger ('POST-FOCUSIN')
+ self.__focus_in()
- self._form.update_record_counter()
- self._form.update_record_status()
self._form.dispatchEvent('gotoENTRY', object=self._form._currentEntry,
_form=self._form)
+
self._form.beginEditing()
# ---------------------------------------------------------------------------
@@ -882,6 +901,32 @@
# ---------------------------------------------------------------------------
+ # Shared code called whenever focus enters or leaves a record
+ # ---------------------------------------------------------------------------
+
+ def __focus_in(self):
+
+ if self._form.get_focus_block() is self:
+ self.processTrigger ('PRE-FOCUSIN')
+
+ self._form.update_record_counter()
+ self._form.update_record_status()
+
+ self.processTrigger ('POST-FOCUSIN')
+
+ # ---------------------------------------------------------------------------
+
+ def __focus_out(self):
+
+ if self._form.get_focus_block() is self:
+ self.processTrigger ('PRE-FOCUSOUT')
+
+ if self.autoCommit and self._resultSet.current:
+ self._form.commit ()
+
+ self.processTrigger ('POST-FOCUSOUT')
+
+ # ---------------------------------------------------------------------------
# Function and Update
# ---------------------------------------------------------------------------
@@ -1466,7 +1511,7 @@
raise StopIteration
else:
- self.block.nextRecord ()
+ self.block.next_record()
if self.block.isEmpty () and self.block._resultSet.isLastRecord ():
self.done = True
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [gnue] r8653 - in trunk/gnue-forms/src: . GFObjects,
reinhard <=